1 /*        $NetBSD: callout.c,v 1.7 2003/03/05 21:05:38 wiz 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 */
15 static int id = 0;
16 static struct timeout_q  *Q = 0; /* pointer to the beginning of timeout queue */
17 
18 static int in_callout = 0;
19 
20 struct 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
29 static void print_Q(void);
30 #else
31 #define   print_Q()
32 #endif
33 
34 int secs_remaining(int);
35 
36 void
callout_init(void)37 callout_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  */
46 void
age_callout_queue(void)47 age_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  */
90 int
timer_setTimer(int delay,cfunc_t action,char * data)91 timer_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 */
153 void
timer_clearTimer(int timer_id)154 timer_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  */
204 static void
print_Q(void)205 print_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 
214 int
secs_remaining(int timer_id)215 secs_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