1 |
root |
1.1 |
/* timers.c - simple timer routines |
2 |
|
|
** |
3 |
|
|
** Copyright © 1995,1998,2000 by Jef Poskanzer <jef@acme.com>. |
4 |
|
|
** All rights reserved. |
5 |
|
|
** |
6 |
|
|
** Redistribution and use in source and binary forms, with or without |
7 |
|
|
** modification, are permitted provided that the following conditions |
8 |
|
|
** are met: |
9 |
|
|
** 1. Redistributions of source code must retain the above copyright |
10 |
|
|
** notice, this list of conditions and the following disclaimer. |
11 |
|
|
** 2. Redistributions in binary form must reproduce the above copyright |
12 |
|
|
** notice, this list of conditions and the following disclaimer in the |
13 |
|
|
** documentation and/or other materials provided with the distribution. |
14 |
|
|
** |
15 |
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
|
|
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
|
|
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
|
|
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
|
|
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
|
|
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
|
|
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
|
|
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
|
|
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
|
|
** SUCH DAMAGE. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include <sys/types.h> |
29 |
|
|
|
30 |
|
|
#include <stdlib.h> |
31 |
|
|
#include <stdio.h> |
32 |
|
|
#include <syslog.h> |
33 |
|
|
|
34 |
|
|
#include "timers.h" |
35 |
|
|
|
36 |
|
|
|
37 |
|
|
#define HASH_SIZE 67 |
38 |
|
|
static Timer* timers[HASH_SIZE]; |
39 |
|
|
static Timer* free_timers; |
40 |
|
|
static int alloc_count, active_count, free_count; |
41 |
|
|
|
42 |
|
|
ClientData JunkClientData; |
43 |
|
|
|
44 |
|
|
|
45 |
|
|
|
46 |
|
|
static unsigned int |
47 |
|
|
hash( Timer* t ) |
48 |
|
|
{ |
49 |
|
|
/* We can hash on the trigger time, even though it can change over |
50 |
|
|
** the life of a timer via either the periodic bit or the tmr_reset() |
51 |
|
|
** call. This is because both of those guys call l_resort(), which |
52 |
|
|
** recomputes the hash and moves the timer to the appropriate list. |
53 |
|
|
*/ |
54 |
|
|
return ( |
55 |
|
|
(unsigned int) t->time.tv_sec ^ |
56 |
|
|
(unsigned int) t->time.tv_usec ) % HASH_SIZE; |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
|
60 |
|
|
static void |
61 |
|
|
l_add( Timer* t ) |
62 |
|
|
{ |
63 |
|
|
int h = t->hash; |
64 |
|
|
register Timer* t2; |
65 |
|
|
register Timer* t2prev; |
66 |
|
|
|
67 |
|
|
t2 = timers[h]; |
68 |
|
|
if ( t2 == (Timer*) 0 ) |
69 |
|
|
{ |
70 |
|
|
/* The list is empty. */ |
71 |
|
|
timers[h] = t; |
72 |
|
|
t->prev = t->next = (Timer*) 0; |
73 |
|
|
} |
74 |
|
|
else |
75 |
|
|
{ |
76 |
|
|
if ( t->time.tv_sec < t2->time.tv_sec || |
77 |
|
|
( t->time.tv_sec == t2->time.tv_sec && |
78 |
|
|
t->time.tv_usec <= t2->time.tv_usec ) ) |
79 |
|
|
{ |
80 |
|
|
/* The new timer goes at the head of the list. */ |
81 |
|
|
timers[h] = t; |
82 |
|
|
t->prev = (Timer*) 0; |
83 |
|
|
t->next = t2; |
84 |
|
|
t2->prev = t; |
85 |
|
|
} |
86 |
|
|
else |
87 |
|
|
{ |
88 |
|
|
/* Walk the list to find the insertion point. */ |
89 |
|
|
for ( t2prev = t2, t2 = t2->next; t2 != (Timer*) 0; |
90 |
|
|
t2prev = t2, t2 = t2->next ) |
91 |
|
|
{ |
92 |
|
|
if ( t->time.tv_sec < t2->time.tv_sec || |
93 |
|
|
( t->time.tv_sec == t2->time.tv_sec && |
94 |
|
|
t->time.tv_usec <= t2->time.tv_usec ) ) |
95 |
|
|
{ |
96 |
|
|
/* Found it. */ |
97 |
|
|
t2prev->next = t; |
98 |
|
|
t->prev = t2prev; |
99 |
|
|
t->next = t2; |
100 |
|
|
t2->prev = t; |
101 |
|
|
return; |
102 |
|
|
} |
103 |
|
|
} |
104 |
|
|
/* Oops, got to the end of the list. Add to tail. */ |
105 |
|
|
t2prev->next = t; |
106 |
|
|
t->prev = t2prev; |
107 |
|
|
t->next = (Timer*) 0; |
108 |
|
|
} |
109 |
|
|
} |
110 |
|
|
} |
111 |
|
|
|
112 |
|
|
|
113 |
|
|
static void |
114 |
|
|
l_remove( Timer* t ) |
115 |
|
|
{ |
116 |
|
|
int h = t->hash; |
117 |
|
|
|
118 |
|
|
if ( t->prev == (Timer*) 0 ) |
119 |
|
|
timers[h] = t->next; |
120 |
|
|
else |
121 |
|
|
t->prev->next = t->next; |
122 |
|
|
if ( t->next != (Timer*) 0 ) |
123 |
|
|
t->next->prev = t->prev; |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
|
127 |
|
|
static void |
128 |
|
|
l_resort( Timer* t ) |
129 |
|
|
{ |
130 |
|
|
/* Remove the timer from its old list. */ |
131 |
|
|
l_remove( t ); |
132 |
|
|
/* Recompute the hash. */ |
133 |
|
|
t->hash = hash( t ); |
134 |
|
|
/* And add it back in to its new list, sorted correctly. */ |
135 |
|
|
l_add( t ); |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
|
139 |
|
|
void |
140 |
|
|
tmr_init( void ) |
141 |
|
|
{ |
142 |
|
|
int h; |
143 |
|
|
|
144 |
|
|
for ( h = 0; h < HASH_SIZE; ++h ) |
145 |
|
|
timers[h] = (Timer*) 0; |
146 |
|
|
free_timers = (Timer*) 0; |
147 |
|
|
alloc_count = active_count = free_count = 0; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
|
151 |
|
|
Timer* |
152 |
|
|
tmr_create( |
153 |
|
|
struct timeval* nowP, TimerProc* timer_proc, ClientData client_data, |
154 |
|
|
long msecs, int periodic ) |
155 |
|
|
{ |
156 |
|
|
Timer* t; |
157 |
|
|
|
158 |
|
|
if ( free_timers != (Timer*) 0 ) |
159 |
|
|
{ |
160 |
|
|
t = free_timers; |
161 |
|
|
free_timers = t->next; |
162 |
|
|
--free_count; |
163 |
|
|
} |
164 |
|
|
else |
165 |
|
|
{ |
166 |
|
|
t = (Timer*) malloc( sizeof(Timer) ); |
167 |
|
|
if ( t == (Timer*) 0 ) |
168 |
|
|
return (Timer*) 0; |
169 |
|
|
++alloc_count; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
t->timer_proc = timer_proc; |
173 |
|
|
t->client_data = client_data; |
174 |
|
|
t->msecs = msecs; |
175 |
|
|
t->periodic = periodic; |
176 |
|
|
if ( nowP != (struct timeval*) 0 ) |
177 |
|
|
t->time = *nowP; |
178 |
|
|
else |
179 |
|
|
(void) gettimeofday( &t->time, (struct timezone*) 0 ); |
180 |
|
|
t->time.tv_sec += msecs / 1000L; |
181 |
|
|
t->time.tv_usec += ( msecs % 1000L ) * 1000L; |
182 |
|
|
if ( t->time.tv_usec >= 1000000L ) |
183 |
|
|
{ |
184 |
|
|
t->time.tv_sec += t->time.tv_usec / 1000000L; |
185 |
|
|
t->time.tv_usec %= 1000000L; |
186 |
|
|
} |
187 |
|
|
t->hash = hash( t ); |
188 |
|
|
/* Add the new timer to the proper active list. */ |
189 |
|
|
l_add( t ); |
190 |
|
|
++active_count; |
191 |
|
|
|
192 |
|
|
return t; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
|
196 |
|
|
struct timeval* |
197 |
|
|
tmr_timeout( struct timeval* nowP ) |
198 |
|
|
{ |
199 |
|
|
long msecs; |
200 |
|
|
static struct timeval timeout; |
201 |
|
|
|
202 |
|
|
msecs = tmr_mstimeout( nowP ); |
203 |
|
|
if ( msecs == INFTIM ) |
204 |
|
|
return (struct timeval*) 0; |
205 |
|
|
timeout.tv_sec = msecs / 1000L; |
206 |
|
|
timeout.tv_usec = ( msecs % 1000L ) * 1000L; |
207 |
|
|
return &timeout; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
|
211 |
|
|
long |
212 |
|
|
tmr_mstimeout( struct timeval* nowP ) |
213 |
|
|
{ |
214 |
|
|
int h; |
215 |
|
|
int gotone; |
216 |
|
|
long msecs, m; |
217 |
|
|
register Timer* t; |
218 |
|
|
|
219 |
|
|
gotone = 0; |
220 |
|
|
msecs = 0; /* make lint happy */ |
221 |
|
|
/* Since the lists are sorted, we only need to look at the |
222 |
|
|
** first timer on each one. |
223 |
|
|
*/ |
224 |
|
|
for ( h = 0; h < HASH_SIZE; ++h ) |
225 |
|
|
{ |
226 |
|
|
t = timers[h]; |
227 |
|
|
if ( t != (Timer*) 0 ) |
228 |
|
|
{ |
229 |
|
|
m = ( t->time.tv_sec - nowP->tv_sec ) * 1000L + |
230 |
|
|
( t->time.tv_usec - nowP->tv_usec ) / 1000L; |
231 |
|
|
if ( ! gotone ) |
232 |
|
|
{ |
233 |
|
|
msecs = m; |
234 |
|
|
gotone = 1; |
235 |
|
|
} |
236 |
|
|
else if ( m < msecs ) |
237 |
|
|
msecs = m; |
238 |
|
|
} |
239 |
|
|
} |
240 |
|
|
if ( ! gotone ) |
241 |
|
|
return INFTIM; |
242 |
|
|
if ( msecs <= 0 ) |
243 |
|
|
msecs = 0; |
244 |
|
|
return msecs; |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
|
248 |
|
|
void |
249 |
|
|
tmr_run( struct timeval* nowP ) |
250 |
|
|
{ |
251 |
|
|
int h; |
252 |
|
|
Timer* t; |
253 |
|
|
Timer* next; |
254 |
|
|
|
255 |
|
|
for ( h = 0; h < HASH_SIZE; ++h ) |
256 |
|
|
for ( t = timers[h]; t != (Timer*) 0; t = next ) |
257 |
|
|
{ |
258 |
|
|
next = t->next; |
259 |
|
|
/* Since the lists are sorted, as soon as we find a timer |
260 |
|
|
** that isn't ready yet, we can go on to the next list. |
261 |
|
|
*/ |
262 |
|
|
if ( t->time.tv_sec > nowP->tv_sec || |
263 |
|
|
( t->time.tv_sec == nowP->tv_sec && |
264 |
|
|
t->time.tv_usec > nowP->tv_usec ) ) |
265 |
|
|
break; |
266 |
|
|
(t->timer_proc)( t->client_data, nowP ); |
267 |
|
|
if ( t->periodic ) |
268 |
|
|
{ |
269 |
|
|
/* Reschedule. */ |
270 |
|
|
t->time.tv_sec += t->msecs / 1000L; |
271 |
|
|
t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; |
272 |
|
|
if ( t->time.tv_usec >= 1000000L ) |
273 |
|
|
{ |
274 |
|
|
t->time.tv_sec += t->time.tv_usec / 1000000L; |
275 |
|
|
t->time.tv_usec %= 1000000L; |
276 |
|
|
} |
277 |
|
|
l_resort( t ); |
278 |
|
|
} |
279 |
|
|
else |
280 |
|
|
tmr_cancel( t ); |
281 |
|
|
} |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
|
285 |
|
|
void |
286 |
|
|
tmr_reset( struct timeval* nowP, Timer* t ) |
287 |
|
|
{ |
288 |
|
|
t->time = *nowP; |
289 |
|
|
t->time.tv_sec += t->msecs / 1000L; |
290 |
|
|
t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; |
291 |
|
|
if ( t->time.tv_usec >= 1000000L ) |
292 |
|
|
{ |
293 |
|
|
t->time.tv_sec += t->time.tv_usec / 1000000L; |
294 |
|
|
t->time.tv_usec %= 1000000L; |
295 |
|
|
} |
296 |
|
|
l_resort( t ); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
|
300 |
|
|
void |
301 |
|
|
tmr_cancel( Timer* t ) |
302 |
|
|
{ |
303 |
|
|
/* Remove it from its active list. */ |
304 |
|
|
l_remove( t ); |
305 |
|
|
--active_count; |
306 |
|
|
/* And put it on the free list. */ |
307 |
|
|
t->next = free_timers; |
308 |
|
|
free_timers = t; |
309 |
|
|
++free_count; |
310 |
|
|
t->prev = (Timer*) 0; |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
|
314 |
|
|
void |
315 |
|
|
tmr_cleanup( void ) |
316 |
|
|
{ |
317 |
|
|
Timer* t; |
318 |
|
|
|
319 |
|
|
while ( free_timers != (Timer*) 0 ) |
320 |
|
|
{ |
321 |
|
|
t = free_timers; |
322 |
|
|
free_timers = t->next; |
323 |
|
|
--free_count; |
324 |
|
|
free( (void*) t ); |
325 |
|
|
--alloc_count; |
326 |
|
|
} |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
|
330 |
|
|
void |
331 |
|
|
tmr_destroy( void ) |
332 |
|
|
{ |
333 |
|
|
int h; |
334 |
|
|
|
335 |
|
|
for ( h = 0; h < HASH_SIZE; ++h ) |
336 |
|
|
while ( timers[h] != (Timer*) 0 ) |
337 |
|
|
tmr_cancel( timers[h] ); |
338 |
|
|
tmr_cleanup(); |
339 |
|
|
} |
340 |
|
|
|
341 |
|
|
|
342 |
|
|
/* Generate debugging statistics syslog message. */ |
343 |
|
|
void |
344 |
|
|
tmr_logstats( long secs ) |
345 |
|
|
{ |
346 |
|
|
syslog( |
347 |
|
|
LOG_NOTICE, " timers - %d allocated, %d active, %d free", |
348 |
|
|
alloc_count, active_count, free_count ); |
349 |
|
|
if ( active_count + free_count != alloc_count ) |
350 |
|
|
syslog( LOG_ERR, "timer counts don't add up!" ); |
351 |
|
|
} |