📄 timer.c
字号:
/* * Cisco router simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * timer.c: Management of timers. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <unistd.h>#include <errno.h>#include <time.h>#include <fcntl.h>#include <signal.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <pthread.h>#include "utils.h"#include "mempool.h"#include "hash.h"#include "timer.h"/* Lock and unlock access to global structures */#define TIMER_LOCK() pthread_mutex_lock(&timer_mutex)#define TIMER_UNLOCK() pthread_mutex_unlock(&timer_mutex)/* Pool of Timer Queues */static timer_queue_t *timer_queue_pool = NULL;/* Hash table to map Timer ID to timer entries */static hash_table_t *timer_id_hash = NULL;/* Last ID used. */static timer_id timer_next_id = 1;/* Mutex to access to global structures (Hash Tables, Pool of queues, ...) */static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;/* Find a timer by its ID */static inline timer_entry_t *timer_find_by_id(timer_id id){ return(hash_table_lookup(timer_id_hash,&id));}/* Allocate a new ID. Disgusting method but it should work. */static inline timer_id timer_alloc_id(void){ while(hash_table_lookup(timer_id_hash,&timer_next_id)) timer_next_id++; return(timer_next_id);}/* Free an ID */static inline void timer_free_id(timer_id id){ hash_table_remove(timer_id_hash,&id);}/* * Select the queue of the pool that has the lowest criticity level. This * is a stupid method. */timer_queue_t *timer_select_queue_from_pool(void){ timer_queue_t *s_queue,*queue; int level; /* to begin, select the first queue of the pool */ s_queue = timer_queue_pool; level = s_queue->level; /* walk through timer queues */ for(queue=timer_queue_pool->next;queue;queue=queue->next) { if (queue->level < level) { level = queue->level; s_queue = queue; } } /* returns selected queue */ return s_queue;}/* Add a timer in a queue */static inline void timer_add_to_queue(timer_queue_t *queue, timer_entry_t *timer){ timer_entry_t *t,*prev = NULL; /* Insert after the last timer with the same or earlier time */ for(t=queue->list;t;t=t->next) { if (t->expire > timer->expire) break; prev = t; } /* Add it in linked list */ timer->next = t; timer->prev = prev; timer->queue = queue; if (timer->next) timer->next->prev = timer; if (timer->prev) timer->prev->next = timer; else queue->list = timer; /* Increment number of timers in queue */ queue->timer_count++; /* Increment criticity level */ queue->level += timer->level;}/* Add a timer in a queue atomically */static inline void timer_add_to_queue_atomic(timer_queue_t *queue, timer_entry_t *timer){ TIMERQ_LOCK(queue); timer_add_to_queue(queue,timer); TIMERQ_UNLOCK(queue);}/* Remove a timer from queue */static inline void timer_remove_from_queue(timer_queue_t *queue, timer_entry_t *timer){ if (timer->prev) timer->prev->next = timer->next; else queue->list = timer->next; if (timer->next) timer->next->prev = timer->prev; timer->next = timer->prev = NULL; /* Decrement number of timers in queue */ queue->timer_count--; /* Decrement criticity level */ queue->level -= timer->level;}/* Remove a timer from a queue atomically */static inline void timer_remove_from_queue_atomic(timer_queue_t *queue,timer_entry_t *timer){ TIMERQ_LOCK(queue); timer_remove_from_queue(queue,timer); TIMERQ_UNLOCK(queue);}/* Free ressources used by a timer */static inline void timer_free(timer_entry_t *timer,int take_lock){ if (take_lock) TIMER_LOCK(); /* Remove ID from hash table */ hash_table_remove(timer_id_hash,&timer->id); if (take_lock) TIMER_UNLOCK(); /* Free memory used by timer */ free(timer);}/* Run timer action */static inline int timer_exec(timer_entry_t *timer){ return(timer->callback(timer->user_arg,timer));}/* Schedule a timer in a queue */static inline void timer_schedule_in_queue(timer_queue_t *queue, timer_entry_t *timer){ m_tmcnt_t current,current_adj; /* Set new expiration date and clear "run" flag */ if (timer->flags & TIMER_BOUNDARY) { current_adj = m_gettime_adj(); current = m_gettime(); timer->expire = current + timer->offset + (timer->interval - (current_adj % timer->interval)); } else timer->expire += timer->interval; timer->flags &= ~TIMER_RUNNING; timer_add_to_queue(queue,timer); }/* Schedule a timer */static int timer_schedule(timer_entry_t *timer){ timer_queue_t *queue; /* Select the least used queue of the pool */ if (!(queue = timer_select_queue_from_pool())) { fprintf(stderr, "timer_schedule: no pool available for timer with ID %llu", timer->id); return(-1); } /* Reschedule it in queue */ TIMERQ_LOCK(queue); timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); return(0);}/* Timer loop */static void *timer_loop(timer_queue_t *queue){ struct timespec t_spc; timer_entry_t *timer; m_tmcnt_t c_time; /* We allow thread cancellation at any time */ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); /* Set signal properties */ m_signal_block(SIGINT); m_signal_block(SIGQUIT); m_signal_block(SIGTERM); for(;;) { /* Prevent asynchronous access problems */ TIMERQ_LOCK(queue); /* Get first event */ timer = queue->list; /* * If we have timers in queue, we setup a timer to wait for first one. * In all cases, thread is woken up when a reschedule occurs. */ if (timer) { t_spc.tv_sec = timer->expire / 1000; t_spc.tv_nsec = (timer->expire % 1000) * 1000000; pthread_cond_timedwait(&queue->schedule,&queue->lock,&t_spc); } else { /* We just wait for reschedule since we don't have any timer */ pthread_cond_wait(&queue->schedule,&queue->lock); } /* We need to check "running" flags to know if we must stop */ if (!queue->running) { TIMERQ_UNLOCK(queue); break; } /* * Now, we need to find why we were woken up. So, we compare current * time with first timer to see if we must execute action associated * with it. */ c_time = m_gettime(); /* Get first event */ timer = queue->list; /* If there is nothing to do for now, wait again */ if ((timer == NULL) || (timer->expire > c_time)) { TIMERQ_UNLOCK(queue); continue; } /* * We have a timer to manage. Remove it from queue and mark it as * running. */ timer_remove_from_queue(queue,timer); timer->flags |= TIMER_RUNNING; /* Execute user function and reschedule timer if required */ if (timer_exec(timer)) timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); } /* Stop thread immediately */ pthread_exit(NULL); return NULL;}/* Remove a timer */int timer_remove(timer_id id){ timer_queue_t *queue = NULL; timer_entry_t *timer; TIMER_LOCK(); /* Find timer */ if (!(timer = timer_find_by_id(id))) { TIMER_UNLOCK(); return(-1); } /* If we have a queue, remove timer from it atomically */ if (timer->queue) { queue = timer->queue; timer_remove_from_queue_atomic(queue,timer); } /* Release timer ID */ timer_free_id(id); /* Free memory used by timer */ free(timer); TIMER_UNLOCK(); /* Signal to this queue that it has been modified */ if (queue) pthread_cond_signal(&queue->schedule); return(0);}/* Enable a timer */static timer_id timer_enable(timer_entry_t *timer){ /* Allocate a new ID */ TIMER_LOCK(); timer->id = timer_alloc_id(); /* Insert ID in hash table */ if (hash_table_insert(timer_id_hash,&timer->id,timer) == -1) { TIMER_UNLOCK(); free(timer); return(0); } /* Schedule event */ if (timer_schedule(timer) == -1) { timer_free(timer,FALSE); timer = NULL; TIMER_UNLOCK(); return(0); } /* Returns timer ID */ TIMER_UNLOCK(); pthread_cond_signal(&timer->queue->schedule); return(timer->id);}/* Create a new timer */timer_id timer_create_entry(m_tmcnt_t interval,int boundary,int level, timer_proc callback,void *user_arg){ timer_entry_t *timer; /* Allocate memory for new timer entry */ if (!(timer = malloc(sizeof(*timer)))) return(0); timer->interval = interval; timer->offset = 0; timer->callback = callback; timer->user_arg = user_arg; timer->flags = 0; timer->level = level; /* Set expiration delay */ if (boundary) { timer->flags |= TIMER_BOUNDARY; } else timer->expire = m_gettime(); return(timer_enable(timer));}/* Create a timer on boundary, with an offset */timer_id timer_create_with_offset(m_tmcnt_t interval,m_tmcnt_t offset, int level,timer_proc callback,void *user_arg){ timer_entry_t *timer; /* Allocate memory for new timer entry */ if (!(timer = malloc(sizeof(*timer)))) return(0); timer->interval = interval; timer->offset = 0; timer->callback = callback; timer->user_arg = user_arg; timer->flags = 0; timer->level = level; timer->flags |= TIMER_BOUNDARY; return(timer_enable(timer));}/* Set a new interval for a timer */int timer_set_interval(timer_id id,long interval){ timer_queue_t *queue; timer_entry_t *timer; TIMER_LOCK(); /* Locate timer */ if (!(timer = timer_find_by_id(id))) { TIMER_UNLOCK(); return(-1); } queue = timer->queue; TIMERQ_LOCK(queue); /* Compute new expiration date */ timer->interval = interval; timer->expire = m_gettime() + (m_tmcnt_t)interval; timer_remove_from_queue(queue,timer); timer_schedule_in_queue(queue,timer); TIMERQ_UNLOCK(queue); TIMER_UNLOCK(); /* Reschedule */ pthread_cond_signal(&queue->schedule); return(0);}/* Create a new timer queue */timer_queue_t *timer_create_queue(void){ timer_queue_t *queue; /* Create new queue structure */ if (!(queue = malloc(sizeof(*queue)))) return NULL; queue->running = TRUE; queue->list = NULL; queue->level = 0; /* Create mutex */ if (pthread_mutex_init(&queue->lock,NULL)) goto error; /* Create condition */ if (pthread_cond_init(&queue->schedule,NULL)) goto error; /* Create thread */ if (pthread_create(&queue->thread,NULL,(void *(*)(void *))timer_loop,queue)) goto error; return queue; error: free(queue); return NULL;}/* Flush queues */void timer_flush_queues(void){ timer_entry_t *timer,*next_timer; timer_queue_t *queue,*next_queue; pthread_t thread; TIMER_LOCK(); for(queue=timer_queue_pool;queue;queue=next_queue) { TIMERQ_LOCK(queue); next_queue = queue->next; thread = queue->thread; /* mark queue as not running */ queue->running = FALSE; /* suppress all timers */ for(timer=queue->list;timer;timer=next_timer) { next_timer = timer->next; timer_free_id(timer->id); free(timer); } TIMERQ_UNLOCK(queue); /* signal changes to the queue thread */ pthread_cond_signal(&queue->schedule); /* wait for thread to terminate */ pthread_join(thread,NULL); pthread_cond_destroy(&queue->schedule); pthread_mutex_destroy(&queue->lock); free(queue); } TIMER_UNLOCK();}/* Add a specified number of queues to the pool */int timer_pool_add_queues(int nr_queues){ timer_queue_t *queue; int i; for(i=0;i<nr_queues;i++) { if (!(queue = timer_create_queue())) return(-1); TIMER_LOCK(); queue->next = timer_queue_pool; timer_queue_pool = queue; TIMER_UNLOCK(); } return(0);}/* Initialize timer sub-system */int timer_init(void){ /* Initialize hash table which maps ID to timer entries */ if (!(timer_id_hash = hash_u64_create(TIMER_HASH_SIZE))) { fprintf(stderr,"timer_init: unable to create hash table."); return(-1); } /* Initialize default queues. If this fails, try to continue. */ if (timer_pool_add_queues(TIMERQ_NUMBER) == -1) { fprintf(stderr, "timer_init: unable to initialize at least one timer queue."); } return(0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -