1/*	$NetBSD: callout.c,v 1.2 2002/12/06 16:02:55 thorpej Exp $	*/
2
3/*
4 * The mrouted program is covered by the license in the accompanying file
5 * named "LICENSE".  Use of the mrouted program represents acceptance of
6 * the terms and conditions listed in that file.
7 *
8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9 * Leland Stanford Junior University.
10 */
11
12#include "defs.h"
13
14/* the code below implements a callout queue */
15static int id = 0;
16static struct timeout_q  *Q = 0; /* pointer to the beginning of timeout queue */
17
18static int in_callout = 0;
19
20struct timeout_q {
21	struct timeout_q *next;		/* next event */
22	int        	 id;
23	cfunc_t          func;    	/* function to call */
24	char	   	 *data;		/* func's data */
25	int            	 time;		/* time offset to next event*/
26};
27
28#ifdef IGMP_DEBUG
29static void print_Q(void);
30#else
31#define	print_Q()
32#endif
33
34int secs_remaining(int);
35
36void
37callout_init(void)
38{
39    Q = (struct timeout_q *) 0;
40}
41
42
43/*
44 * signal handler for SIGALARM that is called once every second
45 */
46void
47age_callout_queue(void)
48{
49    struct timeout_q *ptr;
50
51    if (in_callout)
52	return;
53
54    in_callout = 1;
55    ptr = Q;
56
57    while (ptr) {
58	if (!ptr->time) {
59	    /* timeout has happened */
60	    Q = Q->next;
61
62	    in_callout = 0;
63	    if (ptr->func)
64		ptr->func(ptr->data);
65	    in_callout = 1;
66
67	    free(ptr);
68	    ptr = Q;
69	}
70	else {
71	    ptr->time --;
72#ifdef IGMP_DEBUG
73	    logit(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time);
74#endif /* IGMP_DEBUG */
75	    in_callout = 0; return;
76	}
77    }
78    in_callout = 0;
79    return;
80}
81
82
83/*
84 * sets the timer
85 *
86 * delay: number of units for timeout
87 * action: function to be called on timeout
88 * data: what to call the timeout function with
89 */
90int
91timer_setTimer(int delay, cfunc_t action, char *data)
92{
93    struct     timeout_q  *ptr, *node, *prev;
94
95    if (in_callout)
96	return -1;
97
98    in_callout = 1;
99
100    /* create a node */
101    node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
102    if (node == 0) {
103	logit(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
104	in_callout = 0;
105	return -1;
106    }
107    node->func = action;
108    node->data = data;
109    node->time = delay;
110    node->next = 0;
111    node->id   = ++id;
112
113    prev = ptr = Q;
114
115    /* insert node in the queue */
116
117    /* if the queue is empty, insert the node and return */
118    if (!Q)
119	Q = node;
120    else {
121	/* chase the pointer looking for the right place */
122	while (ptr) {
123
124	    if (delay < ptr->time) {
125		/* right place */
126
127		node->next = ptr;
128		if (ptr == Q)
129		    Q = node;
130		else
131		    prev->next = node;
132		ptr->time -= node->time;
133		print_Q();
134		in_callout = 0;
135		return node->id;
136	    } else  {
137		/* keep moving */
138
139		delay -= ptr->time; node->time = delay;
140		prev = ptr;
141		ptr = ptr->next;
142	    }
143	}
144	prev->next = node;
145    }
146    print_Q();
147    in_callout = 0;
148    return node->id;
149}
150
151
152/* clears the associated timer */
153void
154timer_clearTimer(int timer_id)
155{
156    struct timeout_q  *ptr, *prev;
157
158    if (in_callout)
159        return;
160    if (!timer_id)
161	return;
162
163    in_callout = 1;
164
165    prev = ptr = Q;
166
167    /*
168     * find the right node, delete it. the subsequent node's time
169     * gets bumped up
170     */
171
172    print_Q();
173    while (ptr) {
174	if (ptr->id == timer_id) {
175	    /* got the right node */
176
177	    /* unlink it from the queue */
178	    if (ptr == Q)
179		Q = Q->next;
180	    else
181		prev->next = ptr->next;
182
183	    /* increment next node if any */
184	    if (ptr->next != 0)
185		(ptr->next)->time += ptr->time;
186
187	    free(ptr->data);
188	    free(ptr);
189	    print_Q();
190	    in_callout = 0;
191	    return;
192	}
193	prev = ptr;
194	ptr = ptr->next;
195    }
196    print_Q();
197    in_callout = 0;
198}
199
200#ifdef IGMP_DEBUG
201/*
202 * debugging utility
203 */
204static void
205print_Q(void)
206{
207    struct timeout_q  *ptr;
208
209    for(ptr = Q; ptr; ptr = ptr->next)
210	logit(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time);
211}
212#endif /* IGMP_DEBUG */
213
214int
215secs_remaining(int timer_id)
216{
217    struct timeout_q  *ptr;
218    int left=0;
219
220    for (ptr = Q; ptr && ptr->id != timer_id; ptr = ptr->next)
221       left += ptr->time;
222
223    if (!ptr) /* not found */
224       return 0;
225
226    return left + ptr->time;
227}
228