/[dynamips]/upstream/dynamips-0.2.6-RC3/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

Annotation of /upstream/dynamips-0.2.6-RC3/timer.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (hide annotations)
Sat Oct 6 16:05:34 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.6-RC2/timer.c
File MIME type: text/plain
File size: 13082 byte(s)
dynamips-0.2.6-RC2

1 dpavlin 3 /*
2     * Cisco 7200 (Predator) 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