⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 timer.c

📁 思科路由器仿真器,用来仿7200系列得,可以在电脑上模拟路由器
💻 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 + -