/[dynamips]/trunk/timer.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/timer.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Sat Oct 6 16:45:40 2007 UTC (11 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 13073 byte(s)
make working copy

1 /*
2 * Cisco router simulation platform.
3 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4 *
5 * timer.c: Management of timers.
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdarg.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <time.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/time.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <netdb.h>
24 #include <pthread.h>
25
26 #include "utils.h"
27 #include "mempool.h"
28 #include "hash.h"
29 #include "timer.h"
30
31 /* Lock and unlock access to global structures */
32 #define TIMER_LOCK() pthread_mutex_lock(&timer_mutex)
33 #define TIMER_UNLOCK() pthread_mutex_unlock(&timer_mutex)
34
35 /* Pool of Timer Queues */
36 static timer_queue_t *timer_queue_pool = NULL;
37
38 /* Hash table to map Timer ID to timer entries */
39 static hash_table_t *timer_id_hash = NULL;
40
41 /* Last ID used. */
42 static timer_id timer_next_id = 1;
43
44 /* Mutex to access to global structures (Hash Tables, Pool of queues, ...) */
45 static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
46
47
48 /* Find a timer by its ID */
49 static inline timer_entry_t *timer_find_by_id(timer_id id)
50 {
51 return(hash_table_lookup(timer_id_hash,&id));
52 }
53
54 /* Allocate a new ID. Disgusting method but it should work. */
55 static inline timer_id timer_alloc_id(void)
56 {
57 while(hash_table_lookup(timer_id_hash,&timer_next_id))
58 timer_next_id++;
59
60 return(timer_next_id);
61 }
62
63 /* Free an ID */
64 static inline void timer_free_id(timer_id id)
65 {
66 hash_table_remove(timer_id_hash,&id);
67 }
68
69 /*
70 * Select the queue of the pool that has the lowest criticity level. This
71 * is a stupid method.
72 */
73 timer_queue_t *timer_select_queue_from_pool(void)
74 {
75 timer_queue_t *s_queue,*queue;
76 int level;
77
78 /* to begin, select the first queue of the pool */
79 s_queue = timer_queue_pool;
80 level = s_queue->level;
81
82 /* walk through timer queues */
83 for(queue=timer_queue_pool->next;queue;queue=queue->next) {
84 if (queue->level < level) {
85 level = queue->level;
86 s_queue = queue;
87 }
88 }
89
90 /* returns selected queue */
91 return s_queue;
92 }
93
94 /* Add a timer in a queue */
95 static inline void timer_add_to_queue(timer_queue_t *queue,
96 timer_entry_t *timer)
97 {
98 timer_entry_t *t,*prev = NULL;
99
100 /* Insert after the last timer with the same or earlier time */
101 for(t=queue->list;t;t=t->next) {
102 if (t->expire > timer->expire) break;
103 prev = t;
104 }
105
106 /* Add it in linked list */
107 timer->next = t;
108 timer->prev = prev;
109 timer->queue = queue;
110
111 if (timer->next)
112 timer->next->prev = timer;
113
114 if (timer->prev)
115 timer->prev->next = timer;
116 else
117 queue->list = timer;
118
119 /* Increment number of timers in queue */
120 queue->timer_count++;
121
122 /* Increment criticity level */
123 queue->level += timer->level;
124 }
125
126 /* Add a timer in a queue atomically */
127 static inline void timer_add_to_queue_atomic(timer_queue_t *queue,
128 timer_entry_t *timer)
129 {
130 TIMERQ_LOCK(queue);
131 timer_add_to_queue(queue,timer);
132 TIMERQ_UNLOCK(queue);
133 }
134
135 /* Remove a timer from queue */
136 static inline void timer_remove_from_queue(timer_queue_t *queue,
137 timer_entry_t *timer)
138 {
139 if (timer->prev)
140 timer->prev->next = timer->next;
141 else
142 queue->list = timer->next;
143
144 if (timer->next)
145 timer->next->prev = timer->prev;
146
147 timer->next = timer->prev = NULL;
148
149 /* Decrement number of timers in queue */
150 queue->timer_count--;
151
152 /* Decrement criticity level */
153 queue->level -= timer->level;
154 }
155
156 /* Remove a timer from a queue atomically */
157 static inline void
158 timer_remove_from_queue_atomic(timer_queue_t *queue,timer_entry_t *timer)
159 {
160 TIMERQ_LOCK(queue);
161 timer_remove_from_queue(queue,timer);
162 TIMERQ_UNLOCK(queue);
163 }
164
165 /* Free ressources used by a timer */
166 static inline void timer_free(timer_entry_t *timer,int take_lock)
167 {
168 if (take_lock) TIMER_LOCK();
169
170 /* Remove ID from hash table */
171 hash_table_remove(timer_id_hash,&timer->id);
172
173 if (take_lock) TIMER_UNLOCK();
174
175 /* Free memory used by timer */
176 free(timer);
177 }
178
179 /* Run timer action */
180 static inline int timer_exec(timer_entry_t *timer)
181 {
182 return(timer->callback(timer->user_arg,timer));
183 }
184
185 /* Schedule a timer in a queue */
186 static inline void timer_schedule_in_queue(timer_queue_t *queue,
187 timer_entry_t *timer)
188 {
189 m_tmcnt_t current,current_adj;
190
191 /* Set new expiration date and clear "run" flag */
192 if (timer->flags & TIMER_BOUNDARY) {
193 current_adj = m_gettime_adj();
194 current = m_gettime();
195
196 timer->expire = current + timer->offset +
197 (timer->interval - (current_adj % timer->interval));
198 } else
199 timer->expire += timer->interval;
200
201 timer->flags &= ~TIMER_RUNNING;
202 timer_add_to_queue(queue,timer);
203 }
204
205 /* Schedule a timer */
206 static int timer_schedule(timer_entry_t *timer)
207 {
208 timer_queue_t *queue;
209
210 /* Select the least used queue of the pool */
211 if (!(queue = timer_select_queue_from_pool())) {
212 fprintf(stderr,
213 "timer_schedule: no pool available for timer with ID %llu",
214 timer->id);
215 return(-1);
216 }
217
218 /* Reschedule it in queue */
219 TIMERQ_LOCK(queue);
220 timer_schedule_in_queue(queue,timer);
221 TIMERQ_UNLOCK(queue);
222 return(0);
223 }
224
225 /* Timer loop */
226 static void *timer_loop(timer_queue_t *queue)
227 {
228 struct timespec t_spc;
229 timer_entry_t *timer;
230 m_tmcnt_t c_time;
231
232 /* We allow thread cancellation at any time */
233 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
234 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
235
236 /* Set signal properties */
237 m_signal_block(SIGINT);
238 m_signal_block(SIGQUIT);
239 m_signal_block(SIGTERM);
240
241 for(;;)
242 {
243 /* Prevent asynchronous access problems */
244 TIMERQ_LOCK(queue);
245
246 /* Get first event */
247 timer = queue->list;
248
249 /*
250 * If we have timers in queue, we setup a timer to wait for first one.
251 * In all cases, thread is woken up when a reschedule occurs.
252 */
253 if (timer) {
254 t_spc.tv_sec = timer->expire / 1000;
255 t_spc.tv_nsec = (timer->expire % 1000) * 1000000;
256 pthread_cond_timedwait(&queue->schedule,&queue->lock,&t_spc);
257 }
258 else {
259 /* We just wait for reschedule since we don't have any timer */
260 pthread_cond_wait(&queue->schedule,&queue->lock);
261 }
262
263 /* We need to check "running" flags to know if we must stop */
264 if (!queue->running) {
265 TIMERQ_UNLOCK(queue);
266 break;
267 }
268
269 /*
270 * Now, we need to find why we were woken up. So, we compare current
271 * time with first timer to see if we must execute action associated
272 * with it.
273 */
274 c_time = m_gettime();
275
276 /* Get first event */
277 timer = queue->list;
278
279 /* If there is nothing to do for now, wait again */
280 if ((timer == NULL) || (timer->expire > c_time)) {
281 TIMERQ_UNLOCK(queue);
282 continue;
283 }
284
285 /*
286 * We have a timer to manage. Remove it from queue and mark it as
287 * running.
288 */
289 timer_remove_from_queue(queue,timer);
290 timer->flags |= TIMER_RUNNING;
291
292 /* Execute user function and reschedule timer if required */
293 if (timer_exec(timer))
294 timer_schedule_in_queue(queue,timer);
295
296 TIMERQ_UNLOCK(queue);
297 }
298
299 /* Stop thread immediately */
300 pthread_exit(NULL);
301 return NULL;
302 }
303
304 /* Remove a timer */
305 int timer_remove(timer_id id)
306 {
307 timer_queue_t *queue = NULL;
308 timer_entry_t *timer;
309
310 TIMER_LOCK();
311
312 /* Find timer */
313 if (!(timer = timer_find_by_id(id))) {
314 TIMER_UNLOCK();
315 return(-1);
316 }
317
318 /* If we have a queue, remove timer from it atomically */
319 if (timer->queue) {
320 queue = timer->queue;
321 timer_remove_from_queue_atomic(queue,timer);
322 }
323
324 /* Release timer ID */
325 timer_free_id(id);
326
327 /* Free memory used by timer */
328 free(timer);
329 TIMER_UNLOCK();
330
331 /* Signal to this queue that it has been modified */
332 if (queue)
333 pthread_cond_signal(&queue->schedule);
334 return(0);
335 }
336
337 /* Enable a timer */
338 static timer_id timer_enable(timer_entry_t *timer)
339 {
340 /* Allocate a new ID */
341 TIMER_LOCK();
342 timer->id = timer_alloc_id();
343
344 /* Insert ID in hash table */
345 if (hash_table_insert(timer_id_hash,&timer->id,timer) == -1) {
346 TIMER_UNLOCK();
347 free(timer);
348 return(0);
349 }
350
351 /* Schedule event */
352 if (timer_schedule(timer) == -1) {
353 timer_free(timer,FALSE);
354 timer = NULL;
355 TIMER_UNLOCK();
356 return(0);
357 }
358
359 /* Returns timer ID */
360 TIMER_UNLOCK();
361 pthread_cond_signal(&timer->queue->schedule);
362 return(timer->id);
363 }
364
365 /* Create a new timer */
366 timer_id timer_create_entry(m_tmcnt_t interval,int boundary,int level,
367 timer_proc callback,void *user_arg)
368 {
369 timer_entry_t *timer;
370
371 /* Allocate memory for new timer entry */
372 if (!(timer = malloc(sizeof(*timer))))
373 return(0);
374
375 timer->interval = interval;
376 timer->offset = 0;
377 timer->callback = callback;
378 timer->user_arg = user_arg;
379 timer->flags = 0;
380 timer->level = level;
381
382 /* Set expiration delay */
383 if (boundary) {
384 timer->flags |= TIMER_BOUNDARY;
385 } else
386 timer->expire = m_gettime();
387
388 return(timer_enable(timer));
389 }
390
391 /* Create a timer on boundary, with an offset */
392 timer_id timer_create_with_offset(m_tmcnt_t interval,m_tmcnt_t offset,
393 int level,timer_proc callback,void *user_arg)
394 {
395 timer_entry_t *timer;
396
397 /* Allocate memory for new timer entry */
398 if (!(timer = malloc(sizeof(*timer))))
399 return(0);
400
401 timer->interval = interval;
402 timer->offset = 0;
403 timer->callback = callback;
404 timer->user_arg = user_arg;
405 timer->flags = 0;
406 timer->level = level;
407 timer->flags |= TIMER_BOUNDARY;
408
409 return(timer_enable(timer));
410 }
411
412 /* Set a new interval for a timer */
413 int timer_set_interval(timer_id id,long interval)
414 {
415 timer_queue_t *queue;
416 timer_entry_t *timer;
417
418 TIMER_LOCK();
419
420 /* Locate timer */
421 if (!(timer = timer_find_by_id(id))) {
422 TIMER_UNLOCK();
423 return(-1);
424 }
425
426 queue = timer->queue;
427
428 TIMERQ_LOCK(queue);
429
430 /* Compute new expiration date */
431 timer->interval = interval;
432 timer->expire = m_gettime() + (m_tmcnt_t)interval;
433
434 timer_remove_from_queue(queue,timer);
435 timer_schedule_in_queue(queue,timer);
436
437 TIMERQ_UNLOCK(queue);
438 TIMER_UNLOCK();
439
440 /* Reschedule */
441 pthread_cond_signal(&queue->schedule);
442 return(0);
443 }
444
445 /* Create a new timer queue */
446 timer_queue_t *timer_create_queue(void)
447 {
448 timer_queue_t *queue;
449
450 /* Create new queue structure */
451 if (!(queue = malloc(sizeof(*queue))))
452 return NULL;
453
454 queue->running = TRUE;
455 queue->list = NULL;
456 queue->level = 0;
457
458 /* Create mutex */
459 if (pthread_mutex_init(&queue->lock,NULL))
460 goto error;
461
462 /* Create condition */
463 if (pthread_cond_init(&queue->schedule,NULL))
464 goto error;
465
466 /* Create thread */
467 if (pthread_create(&queue->thread,NULL,(void *(*)(void *))timer_loop,queue))
468 goto error;
469
470 return queue;
471
472 error:
473 free(queue);
474 return NULL;
475 }
476
477 /* Flush queues */
478 void timer_flush_queues(void)
479 {
480 timer_entry_t *timer,*next_timer;
481 timer_queue_t *queue,*next_queue;
482 pthread_t thread;
483
484 TIMER_LOCK();
485
486 for(queue=timer_queue_pool;queue;queue=next_queue)
487 {
488 TIMERQ_LOCK(queue);
489 next_queue = queue->next;
490 thread = queue->thread;
491
492 /* mark queue as not running */
493 queue->running = FALSE;
494
495 /* suppress all timers */
496 for(timer=queue->list;timer;timer=next_timer) {
497 next_timer = timer->next;
498 timer_free_id(timer->id);
499 free(timer);
500 }
501
502 TIMERQ_UNLOCK(queue);
503
504 /* signal changes to the queue thread */
505 pthread_cond_signal(&queue->schedule);
506
507 /* wait for thread to terminate */
508 pthread_join(thread,NULL);
509
510 pthread_cond_destroy(&queue->schedule);
511 pthread_mutex_destroy(&queue->lock);
512 free(queue);
513 }
514
515 TIMER_UNLOCK();
516 }
517
518 /* Add a specified number of queues to the pool */
519 int timer_pool_add_queues(int nr_queues)
520 {
521 timer_queue_t *queue;
522 int i;
523
524 for(i=0;i<nr_queues;i++)
525 {
526 if (!(queue = timer_create_queue()))
527 return(-1);
528
529 TIMER_LOCK();
530 queue->next = timer_queue_pool;
531 timer_queue_pool = queue;
532 TIMER_UNLOCK();
533 }
534
535 return(0);
536 }
537
538 /* Initialize timer sub-system */
539 int timer_init(void)
540 {
541 /* Initialize hash table which maps ID to timer entries */
542 if (!(timer_id_hash = hash_u64_create(TIMER_HASH_SIZE))) {
543 fprintf(stderr,"timer_init: unable to create hash table.");
544 return(-1);
545 }
546
547 /* Initialize default queues. If this fails, try to continue. */
548 if (timer_pool_add_queues(TIMERQ_NUMBER) == -1) {
549 fprintf(stderr,
550 "timer_init: unable to initialize at least one timer queue.");
551 }
552
553 return(0);
554 }

  ViewVC Help
Powered by ViewVC 1.1.26