• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • Directories
  • File List
  • Globals

timer.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of SIP-router, a free SIP server.
00007  *
00008  * SIP-router is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * SIP-router is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 /* History:
00023  * --------
00024  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
00025  *  2003-03-29  cleaning pkg_mallocs introduced (jiri)
00026  *  2005-07-27  complete re-design/re-implementation (andrei)
00027  *  2005-12-12  workaround & bug reporting for timer_del(self) called from
00028  *              a timer handle; added timer_allow_del()  (andrei)
00029  *  2007-05-26  workaround for darwin sigwait() bug, see slow_timer_main() or
00030  *              grep __OS_darwin for more info (andrei)
00031  *  2007-07-01  timer_del() returns <0 if the timer is not active or 
00032  *               cannot be deleted (andrei)
00033  */
00034 
00035 
00043 #include "timer.h"
00044 #include "timer_funcs.h"
00045 #include "timer_ticks.h"
00046 #include "dprint.h"
00047 #include <time.h>     /* gettimeofday */
00048 #include <sys/time.h> /* setitimer, gettimeofday */
00049 #include <signal.h>   /* SIGALRM */
00050 #include <errno.h>
00051 #include <unistd.h> /* pause() */
00052 #include <stdlib.h> /* random, debugging only */
00053 #include "error.h"
00054 #include "signals.h"
00055 /*
00056 #include "config.h"
00057 */
00058 #include "globals.h"
00059 #include "mem/mem.h"
00060 #ifdef SHM_MEM
00061 #include "mem/shm_mem.h"
00062 #endif
00063 #include "locking.h"
00064 #include "sched_yield.h"
00065 #include "cfg/cfg_struct.h"
00066 
00067 
00068 /* how often will the timer handler be called (in ticks) */
00069 #define TIMER_HANDLER_INTERVAL  1U
00070 /* how often to try to re-adjust the ticks */
00071 #define TIMER_RESYNC_TICKS      (TIMER_TICKS_HZ*5U)  /* each 5 s */
00072 #define TIMER_MAX_DRIFT (TIMER_TICKS_HZ/10U) /* if drift > 0.1s adjust */
00073 
00074 
00075 
00076 static ticks_t* ticks=0;
00077 static ticks_t last_ticks; /* last time we adjusted the time */
00078 static ticks_t last_adj_check; /* last time we ran adjust_ticks */
00079 static ticks_t prev_ticks; /* last time we ran the timer, also used as
00080                                                           "current" ticks when running the timer for
00081                                                           "skipped" ticks */
00082 
00083 static struct timeval last_time;
00084 static struct timeval start_time; /* for debugging */
00085 
00086 static volatile int run_timer=0;
00087 static int timer_id=0;
00088 
00089 static gen_lock_t* timer_lock=0;
00090 static struct timer_ln* volatile* running_timer=0;/* running timer handler */
00091 static int in_timer=0;
00092 
00093 #define IS_IN_TIMER() (in_timer)
00094 
00095 #define LOCK_TIMER_LIST()               lock_get(timer_lock)
00096 #define UNLOCK_TIMER_LIST()             lock_release(timer_lock)
00097 
00098 /* we can get away without atomic_set/atomic_cmp and write barriers because we
00099  * always call SET_RUNNING and IS_RUNNING while holding the timer lock
00100  * => it's implicitly atomic and the lock acts as write barrier */
00101 #define SET_RUNNING(t)          (*running_timer=(t))
00102 #define IS_RUNNING(t)           (*running_timer==(t))
00103 #define UNSET_RUNNING()         (*running_timer=0)
00104 
00105 #ifdef USE_SLOW_TIMER
00106 
00107 #define SLOW_TIMER_SIG  SIGUSR2
00108 /* timer flags checks */
00109 #define IS_FAST_TIMER(t)        (t->flags&F_TIMER_FAST)
00110 #define SET_SLOW_LIST(t)        (t->flags|=F_TIMER_ON_SLOW_LIST)
00111 #define RESET_SLOW_LIST(t)      (t->flags&=~F_TIMER_ON_SLOW_LIST)
00112 #define IS_ON_SLOW_LIST(t)      (t->flags&F_TIMER_ON_SLOW_LIST)
00113 
00114 #define SLOW_LISTS_NO   1024U  /* slow lists number, 2^k recommended */
00115 
00116 
00117 static gen_lock_t*  slow_timer_lock; /* slow timer lock */
00118 static struct timer_head* slow_timer_lists; 
00119 static volatile unsigned short* t_idx; /* "main" timer index in slow_lists[] */
00120 static volatile unsigned short* s_idx; /* "slow" timer index in slow_lists[] */
00121 static struct timer_ln* volatile* running_timer2=0; /* timer handler running
00122                                                                                                              in the "slow" timer */
00123 static sigset_t slow_timer_sset;
00124 pid_t slow_timer_pid;
00125 static int in_slow_timer=0;
00126 
00127 #define IS_IN_TIMER_SLOW() (in_slow_timer)
00128 #define SET_RUNNING_SLOW(t)             (*running_timer2=(t))
00129 #define IS_RUNNING_SLOW(t)              (*running_timer2==(t))
00130 #define UNSET_RUNNING_SLOW()    (*running_timer2=0)
00131 
00132 #define LOCK_SLOW_TIMER_LIST()          lock_get(slow_timer_lock)
00133 #define UNLOCK_SLOW_TIMER_LIST()        lock_release(slow_timer_lock)
00134 
00135 
00136 #endif
00137 
00138 
00139 struct timer_lists* timer_lst=0;
00140 
00141 void sig_timer(int signo)
00142 {
00143         (*ticks)++;
00144         if (( *ticks % TIMER_HANDLER_INTERVAL)==0){
00145                 /* set a flag to run the handler */
00146                 run_timer=1;
00147         }
00148 }
00149 
00150 
00151 
00152 void destroy_timer()
00153 {
00154         struct itimerval it;
00155         
00156         /* disable timer */
00157         memset(&it, 0, sizeof(it));
00158         setitimer(ITIMER_REAL, &it, 0); 
00159         set_sig_h(SIGALRM, SIG_IGN);
00160         if (timer_lock){
00161                 lock_destroy(timer_lock);
00162                 lock_dealloc(timer_lock);
00163                 timer_lock=0;
00164         }
00165         if (ticks){
00166 #ifdef SHM_MEM
00167                 shm_free(ticks);
00168 #else
00169                 pkg_free(ticks);
00170 #endif
00171                 ticks=0;
00172         }
00173         if (timer_lst){
00174 #ifdef SHM_MEM
00175                 shm_free(timer_lst);
00176 #else
00177                 pkg_free(timer_lst);
00178 #endif
00179                 timer_lst=0;
00180         }
00181         if (running_timer){
00182                 shm_free((void*)running_timer);
00183                 running_timer=0;
00184         }
00185 #ifdef USE_SLOW_TIMER
00186         if (slow_timer_lock){
00187                 lock_destroy(slow_timer_lock);
00188                 lock_dealloc(slow_timer_lock);
00189                 slow_timer_lock=0;
00190         }
00191         if (slow_timer_lists){
00192                 shm_free((void*)slow_timer_lists);
00193                 slow_timer_lists=0;
00194         }
00195         if (t_idx){
00196                 shm_free((void*)t_idx);
00197                 t_idx=0;
00198         }
00199         if (s_idx){
00200                 shm_free((void*)s_idx);
00201                 s_idx=0;
00202         }
00203         if(running_timer2){
00204                 shm_free((void*)running_timer2);
00205                 running_timer2=0;
00206         }
00207 #endif
00208 }
00209 
00210 
00211 
00212 /* ret 0 on success, <0 on error*/
00213 int init_timer()
00214 {
00215         int r;
00216         int ret;
00217         
00218         ret=-1;
00219         
00220         /* init the locks */
00221         timer_lock=lock_alloc();
00222         if (timer_lock==0){
00223                 ret=E_OUT_OF_MEM;
00224                 goto error;
00225         }
00226         if (lock_init(timer_lock)==0){
00227                 lock_dealloc(timer_lock);
00228                 timer_lock=0;
00229                 ret=-1;
00230                 goto error;
00231         }
00232         /* init the shared structs */
00233 #ifdef SHM_MEM
00234         ticks=shm_malloc(sizeof(ticks_t));
00235         timer_lst=shm_malloc(sizeof(struct timer_lists));
00236 #else
00237         /* in this case get_ticks won't work! */
00238         LOG(L_INFO, "WARNING: no shared memory support compiled in"
00239                                 " get_ticks won't work\n");
00240         ticks=pkg_malloc(sizeof(ticks_t));
00241         timer_lst=pkg_malloc(sizeof(struct timer_lists));
00242 #endif
00243         if (ticks==0){
00244                 LOG(L_CRIT, "ERROR: init_timer: out of shared memory (ticks)\n");
00245                 ret=E_OUT_OF_MEM;
00246                 goto error;
00247         }
00248         if (timer_lst==0){
00249                 LOG(L_CRIT, "ERROR: init_timer: out of shared memory (timer_lst)\n");
00250                 ret=E_OUT_OF_MEM;
00251                 goto error;
00252         }
00253         running_timer=shm_malloc(sizeof(struct timer_ln*));
00254         if (running_timer==0){
00255                 LOG(L_CRIT, "ERROR: init_timer: out of memory (running_timer)\n");
00256                 ret=E_OUT_OF_MEM;
00257                 goto error;
00258         }
00259 
00260         /* initial values */
00261         memset(timer_lst, 0, sizeof(struct timer_lists));
00262         *ticks=random(); /* random value for start, for debugging */
00263         prev_ticks=last_ticks=last_adj_check=*ticks;
00264         *running_timer=0;
00265         if (gettimeofday(&start_time, 0)<0){
00266                 LOG(L_ERR, "ERROR: init_timer: gettimeofday failed: %s [%d]\n",
00267                                 strerror(errno), errno);
00268                 ret=-1;
00269                 goto error;
00270         }
00271         last_time=start_time;
00272         DBG("init_timer: starting with *ticks=%u\n", (unsigned) *ticks);
00273         
00274         /* init timer structures */
00275         for (r=0; r<H0_ENTRIES; r++)
00276                 _timer_init_list(&timer_lst->h0[r]);
00277         for (r=0; r<H1_ENTRIES; r++)
00278                 _timer_init_list(&timer_lst->h1[r]);
00279         for (r=0; r<H2_ENTRIES; r++)
00280                 _timer_init_list(&timer_lst->h2[r]);
00281         _timer_init_list(&timer_lst->expired);
00282         
00283 #ifdef USE_SLOW_TIMER
00284         
00285         /* init the locks */
00286         slow_timer_lock=lock_alloc();
00287         if (slow_timer_lock==0){
00288                 ret=E_OUT_OF_MEM;
00289                 goto error;
00290         }
00291         if (lock_init(slow_timer_lock)==0){
00292                 lock_dealloc(slow_timer_lock);
00293                 slow_timer_lock=0;
00294                 ret=-1;
00295                 goto error;
00296         }
00297         t_idx=shm_malloc(sizeof(*t_idx));
00298         s_idx=shm_malloc(sizeof(*s_idx));
00299         slow_timer_lists=shm_malloc(sizeof(struct timer_head)*SLOW_LISTS_NO);
00300         running_timer2=shm_malloc(sizeof(struct timer_ln*));
00301         if ((t_idx==0)||(s_idx==0) || (slow_timer_lists==0) ||(running_timer2==0)){
00302                 LOG(L_ERR, "ERROR: init_timer: out of shared memory (slow)\n");
00303                 ret=E_OUT_OF_MEM;
00304                 goto error;
00305         }
00306         *t_idx=*s_idx=0;
00307         *running_timer2=0;
00308         for (r=0; r<SLOW_LISTS_NO; r++)
00309                 _timer_init_list(&slow_timer_lists[r]);
00310         
00311 #endif
00312         
00313         DBG("init_timer: timer_list between %p and %p\n",
00314                         &timer_lst->h0[0], &timer_lst->h2[H2_ENTRIES]);
00315         return 0;
00316 error:
00317         destroy_timer();
00318         return ret;
00319 }
00320 
00321 
00322 
00323 #ifdef USE_SLOW_TIMER
00324 /* arm the "slow" timer ( start it) 
00325  * returns -1 on error
00326  * WARNING: use it in the same process as the timer
00327  *  (the one using pause(); timer_handler()) or
00328  *  change run_timer to a pointer in shared mem */
00329 int arm_slow_timer()
00330 {
00331         sigemptyset(&slow_timer_sset);
00332         sigaddset(&slow_timer_sset, SLOW_TIMER_SIG);
00333 again:
00334         if (sigprocmask(SIG_BLOCK, &slow_timer_sset, 0)==-1){
00335                 if (errno==EINTR) goto again;
00336                 LOG(L_ERR, "ERROR: arm_slow_timer: sigprocmask failed: %s [%d]}n",
00337                                 strerror(errno), errno);
00338                 goto error;
00339         }
00340 #ifdef __OS_darwin
00341         /* workaround for darwin sigwait bug, see slow_timer_main() for more
00342            info (or grep __OS_darwin) */
00343         /* keep in sync wih main.c: sig_usr() - signals we are interested in */
00344         sigaddset(&slow_timer_sset, SIGINT);
00345         sigaddset(&slow_timer_sset, SIGTERM);
00346         sigaddset(&slow_timer_sset, SIGUSR1);
00347         sigaddset(&slow_timer_sset, SIGHUP);
00348         sigaddset(&slow_timer_sset, SIGCHLD);
00349         sigaddset(&slow_timer_sset, SIGALRM);
00350 #endif
00351         /* initialize the config framework */
00352         if (cfg_child_init()) goto error;
00353 
00354         return 0;
00355 error:
00356         return -1;
00357 }
00358 #endif
00359 
00360 
00361 
00362 
00363 /* arm the timer ( start it) 
00364  * returns -1 on error
00365  * WARNING: use it in the same process as the timer
00366  *  (the one using pause(); timer_handler()) or
00367  *  change run_timer to a pointer in shared mem */
00368 int arm_timer()
00369 {
00370         struct itimerval it;
00371         /* init signal generation */
00372         it.it_interval.tv_sec=0;
00373         it.it_interval.tv_usec=1000000/TIMER_TICKS_HZ;
00374         it.it_value=it.it_interval;
00375         /* install the signal handler */
00376         if (set_sig_h(SIGALRM, sig_timer) == SIG_ERR ){
00377                 LOG(L_CRIT, "ERROR: init_timer: the SIGALRM signal handler cannot"
00378                                         " be installed: %s [%d]\n", strerror(errno), errno);
00379                 return -1;
00380         }
00381         if (setitimer(ITIMER_REAL, &it, 0) == -1){
00382                 LOG(L_CRIT, "ERROR: init_timer: setitimer failed: %s [%d]\n",
00383                                         strerror(errno), errno);
00384                 return -1;
00385         }
00386         if (gettimeofday(&last_time, 0)<0){
00387                 LOG(L_ERR, "ERROR: arm_timer: gettimeofday failed: %s [%d]\n",
00388                                 strerror(errno), errno);
00389                 return -1;
00390         }
00391         /* initialize the config framework */
00392         if (cfg_child_init()) return -1;
00393 
00394         return 0;
00395 }
00396 
00397 
00398 
00399 #ifdef DBG_ser_time
00400 /* debugging  only */
00401 void check_ser_drift();
00402 #endif /* DBG_set_time */
00403 
00404 
00405 
00406 /* adjust the timer using the "real" time, each TIMER_RESYNC_TICKS, but only
00407  * if timer drift > TIMER_MAX_DRIFT
00408  * NOTES: - it will adjust time within  TIMER_MAX_DRIFT from the "real"
00409  *          elapsed time
00410  *        - it will never decrease the *ticks, only increase it (monotonic)
00411  *        - it works ok as long as the adjustment interval < MAX_TICKS_T
00412  * -- andrei
00413  */
00414 inline static void adjust_ticks(void)
00415 {
00416         struct timeval crt_time;
00417         long long diff_time;
00418         ticks_t diff_time_ticks;
00419         ticks_t diff_ticks_raw;
00420         s_ticks_t delta;
00421         
00422         /* fix ticks if necessary */
00423         if ((*ticks-last_adj_check)>=(ticks_t)TIMER_RESYNC_TICKS){
00424 #ifdef DBG_ser_time
00425                 check_ser_drift();
00426 #endif /* DBG_ser_time */
00427                 last_adj_check=*ticks;
00428                 if (gettimeofday(&crt_time, 0)<0){
00429                         LOG(L_ERR, "ERROR: adjust_ticks: gettimeofday failed: %s [%d]\n",
00430                                 strerror(errno), errno);
00431                         return; /* ignore */
00432                 }
00433                 diff_time=(long long)crt_time.tv_sec*1000000+crt_time.tv_usec-
00434                                         ((long long) last_time.tv_sec*1000000+last_time.tv_usec);
00435                 if (diff_time<0){
00436                         LOG(L_WARN, "WARNING: time changed backwards %ld ms ignoring...\n",
00437                                                 (long)(diff_time/1000));
00438                         last_time=crt_time;
00439                         last_ticks=*ticks;
00440                 }else{
00441                         diff_ticks_raw=*ticks-last_ticks;
00442                         diff_time_ticks=(ticks_t)((diff_time*TIMER_TICKS_HZ)/1000000LL);
00443                         delta=(s_ticks_t)(diff_time_ticks-diff_ticks_raw);
00444                         if (delta<-1){
00445                                 LOG(L_WARN, "WARNING: our timer runs faster then real-time"
00446                                                 " (%lu ms / %u ticks our time .->"
00447                                                  " %lu ms / %u ticks real time)\n", 
00448                                                 (unsigned long)(diff_ticks_raw*1000L/TIMER_TICKS_HZ),
00449                                                 diff_ticks_raw,
00450                                                 (unsigned long)(diff_time/1000), diff_time_ticks);
00451                                 last_time=crt_time;
00452                                 last_ticks=*ticks;
00453                         }else{
00454                                 /* fix the ticks */
00455                                 if (delta>(s_ticks_t)TIMER_MAX_DRIFT){
00456 #ifndef TIMER_DEBUG
00457                                         if (delta > 2*(s_ticks_t)TIMER_MAX_DRIFT+1)
00458 #endif
00459                                                 DBG("adjusting timer ticks (%lu) with %ld ms"
00460                                                                 " (%ld ticks)\n",
00461                                                                 (unsigned long)*ticks,
00462                                                         (long)(delta*1000)/TIMER_TICKS_HZ, (long)delta);
00463                                         *ticks+=(ticks_t)delta;
00464                                 }else{
00465                                         /*DBG("incredible, but our timer is in sync with"
00466                                                         " real time (%lu)\n", (unsigned long)*ticks);
00467                                         */
00468                                 }
00469                         }
00470                 }
00471         }
00472 }
00473 
00474 
00475 
00476 /* time(2) equivalent, using ser internal timers (faster then a syscall) */
00477 time_t ser_time(time_t *t)
00478 {
00479         if (likely(t==0))
00480                 return last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
00481         *t=last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
00482         return *t;
00483 }
00484 
00485 
00486 
00487 /* gettimeofday(2) equivalent, using ser internal timers (faster 
00488  * but more imprecise)
00489  * WARNING: ignores tz (it's obsolete anyway)*/
00490 int ser_gettimeofday(struct timeval* tv, struct timezone* tz)
00491 {
00492         if (likely(tv!=0)){
00493                 tv->tv_sec=last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
00494                 tv->tv_usec=last_time.tv_usec+
00495                                         (TICKS_TO_MS(*ticks-last_ticks)%1000)*1000;
00496         }
00497         return 0;
00498 }
00499 
00500 
00501 
00502 #ifdef DBG_ser_time
00503 /* debugging  only, remove */
00504 void check_ser_drift()
00505 {
00506         time_t t1, t2;
00507         struct timeval tv1, tv2;
00508         int r;
00509         
00510         t1=time(0);
00511         t2=ser_time(0);
00512         if (t1!=t2)
00513                 BUG("time(0)!=ser_time(0) : %d != %d \n", (unsigned)t1, (unsigned)t2);
00514         
00515         r=gettimeofday(&tv1, 0);
00516         ser_gettimeofday(&tv2, 0);
00517         if (tv1.tv_sec!=tv2.tv_sec)
00518                 BUG("gettimeofday seconds!=ser_gettimeofday seconds : %d != %d \n",
00519                                 (unsigned)tv1.tv_sec, (unsigned)tv2.tv_sec);
00520         else if ((tv1.tv_usec > tv2.tv_usec) && 
00521                                 (unsigned)(tv1.tv_usec-tv2.tv_usec)>100000)
00522                 BUG("gettimeofday usecs > ser_gettimeofday with > 0.1s : %d ms\n",
00523                         (unsigned)(tv1.tv_usec-tv2.tv_usec)/1000);
00524         else if ((tv1.tv_usec < tv2.tv_usec) && 
00525                                 (unsigned)(tv2.tv_usec-tv1.tv_usec)>100000)
00526                 BUG("gettimeofday usecs < ser_gettimeofday with > 0.1s : %d ms\n",
00527                         (unsigned)(tv2.tv_usec-tv1.tv_usec)/1000);
00528 }
00529 #endif /* DBG_ser_time */
00530 
00531 
00532 
00533 struct timer_ln* timer_alloc()
00534 {
00535         return shm_malloc(sizeof(struct timer_ln));
00536 }
00537 
00538 void timer_free(struct timer_ln* t)
00539 {
00540         shm_free(t);
00541 }
00542 
00543 
00544 /* unsafe (no lock ) timer add function
00545  * t = current ticks
00546  * tl must be filled (the intial_timeout and flags must be set)
00547  * returns -1 on error, 0 on success */
00548 static inline int _timer_add(ticks_t t, struct timer_ln* tl)
00549 {
00550         ticks_t delta;
00551 
00552 #ifdef USE_SLOW_TIMER
00553         tl->flags&=~((unsigned short)F_TIMER_ON_SLOW_LIST);
00554         tl->slow_idx=0;
00555 #endif
00556         delta=tl->initial_timeout;
00557         tl->expire=t+delta;
00558         return _timer_dist_tl(tl, delta);
00559 }
00560 
00561 
00562 
00563 /* "public", safe timer add functions
00564  * adds a timer at delta ticks from the current time
00565  * returns -1 on error, 0 on success
00566  * WARNING: to re-add an expired or deleted timer you must call
00567  *          timer_reinit(tl) prior to timer_add
00568  *          The default behaviour allows timer_add to add a timer only if it
00569  *          has never been added before.
00570  */
00571 #ifdef TIMER_DEBUG
00572 int timer_add_safe(struct timer_ln* tl, ticks_t delta,
00573                                         const char* file, const char* func, unsigned line)
00574 #else
00575 int timer_add_safe(struct timer_ln* tl, ticks_t delta)
00576 #endif
00577 {
00578         int ret;
00579         
00580         LOCK_TIMER_LIST();
00581         if (tl->flags & F_TIMER_ACTIVE){
00582 #ifdef TIMER_DEBUG
00583                 LOG(timerlog, "timer_add called on an active timer %p (%p, %p),"
00584                                         " flags %x\n", tl, tl->next, tl->prev, tl->flags);
00585                 LOG(timerlog, "WARN: -timer_add-; called from %s(%s):%d\n",
00586                                         func, file, line);
00587                 LOG(timerlog, "WARN: -timer_add-: added %d times"
00588                                         ", last from: %s(%s):%d, deleted %d times"
00589                                         ", last from: %s(%s):%d, init %d times, expired %d \n",
00590                                         tl->add_calls, tl->add_func, tl->add_file, tl->add_line,
00591                                         tl->del_calls, tl->del_func, tl->del_file, tl->del_line,
00592                                         tl->init, tl->expires_no);
00593 #else
00594                 DBG("timer_add called on an active timer %p (%p, %p),"
00595                                         " flags %x\n", tl, tl->next, tl->prev, tl->flags);
00596 #endif
00597                 ret=-1; /* refusing to add active or non-reinit. timer */
00598                 goto error;
00599         }
00600         tl->initial_timeout=delta;
00601         if ((tl->next!=0) || (tl->prev!=0)){
00602                 LOG(L_CRIT, "BUG: timer_add: called with linked timer: %p (%p, %p)\n",
00603                                 tl, tl->next, tl->prev);
00604                 ret=-1;
00605                 goto error;
00606         }
00607         tl->flags|=F_TIMER_ACTIVE;
00608 #ifdef TIMER_DEBUG
00609         tl->add_file=file;
00610         tl->add_func=func;
00611         tl->add_line=line;
00612         tl->add_calls++;
00613 #endif
00614         ret=_timer_add(*ticks, tl);
00615 error:
00616         UNLOCK_TIMER_LIST();
00617         return ret;
00618 }
00619 
00620 
00621 
00622 /* safe timer delete
00623  * deletes tl and inits the list pointer to 0
00624  * returns  <0 on error (-1 if timer not active/already deleted and -2 if 
00625  *           delete attempted from the timer handler) and 0 on success
00626  */
00627 #ifdef TIMER_DEBUG
00628 int timer_del_safe(struct timer_ln* tl,
00629                                         const char* file, const char* func, unsigned line)
00630 #else
00631 int timer_del_safe(struct timer_ln* tl)
00632 #endif
00633 {
00634         int ret;
00635         
00636         ret=-1;
00637 again:
00638         /* quick exit if timer inactive */
00639         if ( !(tl->flags & F_TIMER_ACTIVE)){
00640 #ifdef TIMER_DEBUG
00641                 LOG(timerlog, "timer_del called on an inactive timer %p (%p, %p),"
00642                                         " flags %x\n", tl, tl->next, tl->prev, tl->flags);
00643                 LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
00644                                         func, file, line);
00645                 LOG(timerlog, "WARN: -timer_del-: added %d times"
00646                                         ", last from: %s(%s):%d, deleted %d times"
00647                                         ", last from: %s(%s):%d, init %d times, expired %d \n",
00648                                         tl->add_calls, tl->add_func, tl->add_file, tl->add_line,
00649                                         tl->del_calls, tl->del_func, tl->del_file, tl->del_line,
00650                                         tl->init, tl->expires_no);
00651 #else
00652 /*
00653                 DBG("timer_del called on an inactive timer %p (%p, %p),"
00654                                         " flags %x\n", tl, tl->next, tl->prev, tl->flags);
00655 */
00656 #endif
00657                 return -1;
00658         }
00659 #ifdef USE_SLOW_TIMER
00660                 if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){
00661                         LOCK_SLOW_TIMER_LIST();
00662                         if (!IS_ON_SLOW_LIST(tl) || (tl->slow_idx==*t_idx)){
00663                                 UNLOCK_SLOW_TIMER_LIST();
00664                                 goto again;
00665                         }
00666                         if (IS_RUNNING_SLOW(tl)){
00667                                 UNLOCK_SLOW_TIMER_LIST();
00668                                 if (IS_IN_TIMER_SLOW()){
00669                                         /* if somebody tries to shoot himself in the foot,
00670                                          * warn him and ignore the delete */
00671                                         LOG(L_CRIT, "BUG: timer handle %p (s) tried to delete"
00672                                                         " itself\n", tl);
00673 #ifdef TIMER_DEBUG
00674                                         LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n",
00675                                                                         func, file, line);
00676                                         LOG(timerlog, "WARN: -timer_del-: added %d times"
00677                                                 ", last from: %s(%s):%d, deleted %d times"
00678                                                 ", last from: %s(%s):%d, init %d times, expired %d \n",
00679                                                 tl->add_calls, tl->add_func, tl->add_file,
00680                                                 tl->add_line, tl->del_calls, tl->del_func, 
00681                                                 tl->del_file, tl->del_line, tl->init, tl->expires_no);
00682 #endif
00683                                         return -2; /* do nothing */
00684                                 }
00685                                 sched_yield(); /* wait for it to complete */
00686                                 goto again;
00687                         }
00688                         if (tl->next!=0){
00689                                 _timer_rm_list(tl); /* detach */
00690                                 tl->next=tl->prev=0;
00691                                 ret=0;
00692 #ifdef TIMER_DEBUG
00693                                 tl->del_file=file;
00694                                 tl->del_func=func;
00695                                 tl->del_line=line;
00696                                 tl->flags|=F_TIMER_DELETED;
00697 #endif
00698                         }else{
00699 #ifdef TIMER_DEBUG
00700                                 LOG(timerlog, "timer_del: (s) timer %p (%p, %p) flags %x "
00701                                                         "already detached\n",
00702                                                         tl, tl->next, tl->prev, tl->flags);
00703                                 LOG(timerlog, "WARN: -timer_del-: @%d tl=%p "
00704                                         "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), 
00705                                         tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
00706                                         tl->data, tl->f, tl->flags);
00707                                 LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
00708                                                 func, file, line);
00709                                 LOG(timerlog, "WARN: -timer_del-: added %d times"
00710                                                 ", last from: %s(%s):%d, deleted %d times"
00711                                                 ", last from: %s(%s):%d, init %d times, expired %d \n",
00712                                                 tl->add_calls,
00713                                                 tl->add_func, tl->add_file, tl->add_line,
00714                                                 tl->del_calls,
00715                                                 tl->del_func, tl->del_file, tl->del_line,
00716                                                 tl->init, tl->expires_no);
00717 #else
00718 /*
00719                                 DBG("timer_del: (s) timer %p (%p, %p) flags %x "
00720                                                         "already detached\n",
00721                                                         tl, tl->next, tl->prev, tl->flags);
00722 */
00723 #endif
00724                                 ret=-1;
00725                         }
00726                         UNLOCK_SLOW_TIMER_LIST();
00727                 }else{
00728 #endif
00729                         LOCK_TIMER_LIST();
00730 #ifdef USE_SLOW_TIMER
00731                         if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){
00732                                 UNLOCK_TIMER_LIST();
00733                                 goto again;
00734                         }
00735 #endif
00736                         if (IS_RUNNING(tl)){
00737                                 UNLOCK_TIMER_LIST();
00738                                 if (IS_IN_TIMER()){
00739                                         /* if somebody tries to shoot himself in the foot,
00740                                          * warn him and ignore the delete */
00741                                         LOG(L_CRIT, "BUG: timer handle %p tried to delete"
00742                                                         " itself\n", tl);
00743 #ifdef TIMER_DEBUG
00744                                         LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n",
00745                                                                         func, file, line);
00746                                         LOG(timerlog, "WARN: -timer_del-: added %d times"
00747                                                 ", last from: %s(%s):%d, deleted %d times"
00748                                                 ", last from: %s(%s):%d, init %d times, expired %d \n",
00749                                                 tl->add_calls, tl->add_func, tl->add_file,
00750                                                 tl->add_line, tl->del_calls, tl->del_func, 
00751                                                 tl->del_file, tl->del_line, tl->init, tl->expires_no);
00752 #endif
00753                                         return -2; /* do nothing */
00754                                 }
00755                                 sched_yield(); /* wait for it to complete */
00756                                 goto again;
00757                         }
00758                         if ((tl->next!=0)&&(tl->prev!=0)){
00759                                 _timer_rm_list(tl); /* detach */
00760                                 tl->next=tl->prev=0;
00761                                 ret=0;
00762 #ifdef TIMER_DEBUG
00763                                 tl->del_file=file;
00764                                 tl->del_func=func;
00765                                 tl->del_line=line;
00766                                 tl->flags|=F_TIMER_DELETED;
00767 #endif
00768                         }else{
00769 #ifdef TIMER_DEBUG
00770                                 LOG(timerlog, "timer_del: (f) timer %p (%p, %p) flags %x "
00771                                                         "already detached\n",
00772                                                         tl, tl->next, tl->prev, tl->flags);
00773                                 LOG(timerlog, "WARN: -timer_del-: @%d tl=%p "
00774                                         "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), 
00775                                         tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
00776                                         tl->data, tl->f, tl->flags);
00777                                 LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
00778                                                 func, file, line);
00779                                 LOG(timerlog, "WARN: -timer_del-: added %d times"
00780                                                 ", last from: %s(%s):%d, deleted %d times"
00781                                                 ", last from: %s(%s):%d, init %d times, expired %d \n",
00782                                                 tl->add_calls,
00783                                                 tl->add_func, tl->add_file, tl->add_line,
00784                                                 tl->del_calls,
00785                                                 tl->del_func, tl->del_file, tl->del_line,
00786                                                 tl->init, tl->expires_no);
00787 #else
00788 /*
00789                                 DBG("timer_del: (f) timer %p (%p, %p) flags %x "
00790                                                         "already detached\n",
00791                                                         tl, tl->next, tl->prev, tl->flags);
00792 */
00793 #endif
00794                                 ret=-1;
00795                         }
00796                         UNLOCK_TIMER_LIST();
00797 #ifdef USE_SLOW_TIMER
00798                 }
00799 #endif
00800 return ret;
00801 }
00802 
00803 
00804 
00805 /* marks a timer as "to be deleted when the handler ends", usefull when
00806  * the timer handler knows it won't prolong the timer anymore (it will 
00807  * return 0) and will do some time consuming work. Calling this function
00808  * will cause simultaneous timer_dels to return immediately (they won't 
00809  * wait anymore for the timer handle to finish). It will also allow 
00810  * self-deleting from the timer handle without bug reports.
00811  * WARNING: - if you rely on timer_del to know when the timer handle execution
00812  *            finishes (e.g. to free resources used in the timer handle), don't
00813  *            use this function.
00814  *          - this function can be called only from a timer handle (in timer
00815  *            context), all other calls will have no effect and will log a
00816  *            bug message
00817  */
00818 void timer_allow_del(void)
00819 {
00820         if (IS_IN_TIMER() ){
00821                         UNSET_RUNNING();
00822         }else
00823 #ifdef USE_SLOW_TIMER
00824         if (IS_IN_TIMER_SLOW()){
00825                         UNSET_RUNNING_SLOW();
00826         }else 
00827 #endif
00828                 LOG(L_CRIT, "BUG: timer_allow_del called outside a timer handle\n");
00829 }
00830 
00831 
00832 /* called from timer_handle, must be called with the timer lock held
00833  * WARNING: expired one shot timers are _not_ automatically reinit
00834  *          (because they could have been already freed from the timer
00835  *           handler so a reinit would not be safe!) */
00836 inline static void timer_list_expire(ticks_t t, struct timer_head* h
00837 #ifdef USE_SLOW_TIMER
00838                                                                                 , struct timer_head* slow_l,
00839                                                                                 slow_idx_t slow_mark
00840 #endif
00841                                                                                                                                         )
00842 {
00843         struct timer_ln * tl;
00844         ticks_t ret;
00845 #ifdef TIMER_DEBUG
00846         struct timer_ln* first;
00847         int i=0;
00848         
00849         first=h->next;
00850 #endif
00851         
00852         /*DBG("timer_list_expire @ ticks = %lu, list =%p\n",
00853                         (unsigned long) *ticks, h);
00854         */
00855         while(h->next!=(struct timer_ln*)h){
00856                 tl=h->next;
00857 #ifdef TIMER_DEBUG /* FIXME: replace w/ EXTRA_DEBUG */
00858                 if (tl==0){
00859                         LOG(L_CRIT, "BUG: timer_list_expire: tl=%p, h=%p {%p, %p}\n",
00860                                         tl, h, h->next, h->prev);
00861                         abort();
00862                 }else if((tl->next==0) || (tl->prev==0)){
00863                         LOG(L_CRIT, "BUG: timer_list_expire: @%d tl=%p "
00864                                         "{ %p, %p, %d, %d, %p, %p, %04x, -},"
00865                                         " h=%p {%p, %p}\n", t, 
00866                                         tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
00867                                         tl->data, tl->f, tl->flags, 
00868                                         h, h->next, h->prev);
00869                         LOG(L_CRIT, "BUG: -timer_list_expire-: cycle %d, first %p,"
00870                                                 "running %p\n", i, first, *running_timer);
00871                         LOG(L_CRIT, "BUG: -timer_list_expire-: added %d times"
00872                                                 ", last from: %s(%s):%d, deleted %d times"
00873                                                 ", last from: %s(%s):%d, init %d times, expired %d \n",
00874                                                 tl->add_calls,
00875                                                 tl->add_func, tl->add_file, tl->add_line,
00876                                                 tl->del_calls,
00877                                                 tl->del_func, tl->del_file, tl->del_line,
00878                                                 tl->init, tl->expires_no);
00879                         abort();
00880                 }
00881                 i++;
00882 #endif
00883                 _timer_rm_list(tl); /* detach */
00884 #ifdef USE_SLOW_TIMER
00885                 if (IS_FAST_TIMER(tl)){
00886 #endif
00887                 /* if fast timer */
00888                         SET_RUNNING(tl);
00889                         tl->next=tl->prev=0; /* debugging */
00890 #ifdef TIMER_DEBUG
00891                         tl->expires_no++;
00892 #endif
00893                         UNLOCK_TIMER_LIST(); /* acts also as write barrier */ 
00894                                 ret=tl->f(t, tl, tl->data);
00895                                 /* reset the configuration group handles */
00896                                 cfg_reset_all();
00897                                 if (ret==0){
00898                                         UNSET_RUNNING();
00899                                         LOCK_TIMER_LIST();
00900                                 }else{
00901                                         /* not one-shot, re-add it */
00902                                         LOCK_TIMER_LIST();
00903                                         if (ret!=(ticks_t)-1) /* ! periodic */
00904                                                 tl->initial_timeout=ret;
00905                                         _timer_add(t, tl);
00906                                         UNSET_RUNNING();
00907                                 }
00908 #ifdef USE_SLOW_TIMER
00909                 }else{
00910                         /* slow timer */
00911                         SET_SLOW_LIST(tl);
00912                         tl->slow_idx=slow_mark; /* current index */
00913                         /* overflow check in timer_handler*/
00914                         _timer_add_list(slow_l, tl);
00915                         
00916                 }
00917 #endif
00918         }
00919 }
00920 
00921 
00922 
00923 /* "main" timer routine
00924  * WARNING: it should never be called twice for the same *ticks value
00925  * (it could cause too fast expires for long timers), *ticks must be also
00926  *  always increasing */
00927 static void timer_handler(void)
00928 {
00929         ticks_t saved_ticks;
00930 #ifdef USE_SLOW_TIMER
00931         int run_slow_timer;
00932         int i;
00933         
00934         run_slow_timer=0;
00935         i=(slow_idx_t)(*t_idx%SLOW_LISTS_NO);
00936 #endif
00937         
00938         /*DBG("timer_handler: called, ticks=%lu, prev_ticks=%lu\n",
00939                         (unsigned long)*ticks, (unsigned long)prev_ticks);
00940         */
00941         run_timer=0; /* reset run_timer */
00942         adjust_ticks();
00943         LOCK_TIMER_LIST();
00944         do{
00945                 saved_ticks=*ticks; /* protect against time running backwards */
00946                 if (prev_ticks>=saved_ticks){
00947                         LOG(L_CRIT, "BUG: timer_handler: backwards or still time\n");
00948                         /* try to continue */
00949                         prev_ticks=saved_ticks-1;
00950                         break;
00951                 }
00952                 /* go through all the "missed" ticks, taking a possible overflow
00953                  * into account */
00954                 for (prev_ticks=prev_ticks+1; prev_ticks!=saved_ticks; prev_ticks++) 
00955                         timer_run(prev_ticks);
00956                 timer_run(prev_ticks); /* do it for saved_ticks too */
00957         }while(saved_ticks!=*ticks); /* in case *ticks changed */
00958 #ifdef USE_SLOW_TIMER
00959         timer_list_expire(*ticks, &timer_lst->expired, &slow_timer_lists[i],
00960                                                 *t_idx);
00961 #else
00962         timer_list_expire(*ticks, &timer_lst->expired);
00963 #endif
00964         /* WARNING: add_timer(...,0) must go directly to expired list, since
00965          * otherwise there is a race between timer running and adding it
00966          * (it could expire it H0_ENTRIES ticks later instead of 'now')*/
00967 #ifdef USE_SLOW_TIMER
00968         if (slow_timer_lists[i].next!=(struct timer_ln*)&slow_timer_lists[i]){
00969                 run_slow_timer=1;
00970                 if ((slow_idx_t)(*t_idx-*s_idx) < (SLOW_LISTS_NO-1U))
00971                         (*t_idx)++;
00972                 else{
00973                         LOG(L_WARN, "slow timer too slow: overflow (%d - %d = %d)\n",
00974                                         *t_idx, *s_idx, *t_idx-*s_idx);
00975                         /* trying to continue */
00976                 }
00977         }
00978 #endif
00979         UNLOCK_TIMER_LIST();
00980 #ifdef USE_SLOW_TIMER
00981         /* wake up the "slow" timer */
00982         if (run_slow_timer)
00983                 kill(slow_timer_pid, SLOW_TIMER_SIG);
00984 #endif
00985 }
00986 
00987 
00988 
00989 /* main timer function, never exists */
00990 void timer_main()
00991 {
00992         in_timer=1; /* mark this process as the fast timer */
00993         while(1){
00994                 if (run_timer){
00995                         /* update the local cfg if needed */
00996                         cfg_update();
00997 
00998                         timer_handler();
00999                 }
01000                 pause();
01001         }
01002 }
01003 
01004 
01005 
01006 /* generic call back for the old style timer functions */
01007 static ticks_t compat_old_handler(ticks_t ti, struct timer_ln* tl,
01008                                                                         void * data)
01009 {
01010         struct sr_timer* t;
01011         
01012 #ifdef TIMER_DEBUG
01013         DBG("timer: compat_old_handler: calling, ticks=%u/%u, tl=%p, t=%p\n",
01014                         prev_ticks, (unsigned)*ticks, tl, data);
01015 #endif
01016         t=(struct sr_timer*)data;
01017         t->timer_f(TICKS_TO_S(*ticks), t->t_param);
01018         return (ticks_t)-1; /* periodic */
01019 }
01020 
01021 
01022 
01023 /* register a periodic timer;
01024  * compatibility mode.w/ the old timer interface...
01025  * ret: <0 on error
01026  * Hint: if you need it in a module, register it from mod_init or it 
01027  * won't work otherwise*/
01028 int register_timer(timer_function f, void* param, unsigned int interval)
01029 {
01030         struct sr_timer* t;
01031 
01032         t=shm_malloc(sizeof(struct sr_timer));
01033         if (t==0){
01034                 LOG(L_ERR, "ERROR: register_timer: out of memory\n");
01035                 goto error;
01036         }
01037         t->id=timer_id++;
01038         t->timer_f=f;
01039         t->t_param=param;
01040         
01041         timer_init(&t->tl, compat_old_handler, t, 0); /* is slow */
01042         if (timer_add(&t->tl, S_TO_TICKS(interval))!=0){
01043                 LOG(L_ERR, "ERROR: register_timer: timer_add failed\n");
01044                 return -1;
01045         }
01046         
01047         return t->id;
01048 
01049 error:
01050         return E_OUT_OF_MEM;
01051 }
01052 
01053 
01054 
01055 ticks_t get_ticks_raw()
01056 {
01057 #ifndef SHM_MEM
01058         LOG(L_CRIT, "WARNING: get_ticks: no shared memory support compiled in"
01059                         ", returning 0 (probably wrong)");
01060         return 0;
01061 #endif
01062         return *ticks;
01063 }
01064 
01065 
01066 
01067 /* returns tick in s (for compatibility with the old code) */
01068 ticks_t get_ticks()
01069 {
01070 #ifndef SHM_MEM
01071         LOG(L_CRIT, "WARNING: get_ticks: no shared memory support compiled in"
01072                         ", returning 0 (probably wrong)");
01073         return 0;
01074 #endif
01075         return TICKS_TO_S(*ticks);
01076 }
01077 
01078 
01079 #ifdef USE_SLOW_TIMER
01080 
01081 
01082 /* slow timer main function, never exists
01083  * This function is intended to be executed in a special separated process
01084  * (the "slow" timer) which will run the timer handlers of all the registered
01085  * timers not marked as "fast". The ideea is to execute the fast timers in the
01086  * "main" timer process, as accurate as possible and defer the execution of the  * timers marked as "slow" to the "slow" timer.
01087  * Implementation details:
01088  *  - it waits for a signal and then wakes up and processes
01089  *    all the lists in slow_timer_lists from [s_idx, t_idx). It will
01090  *   -it  increments *s_idx (at the end it will be == *t_idx)
01091  *   -all list operations are protected by the "slow" timer lock
01092  */
01093 #ifdef __OS_darwin
01094 extern void sig_usr(int signo);
01095 #endif
01096 
01097 void slow_timer_main()
01098 {
01099         int n;
01100         ticks_t ret;
01101         struct timer_ln* tl;
01102         unsigned short i;
01103 #ifdef USE_SIGWAIT
01104         int sig;
01105 #endif
01106         
01107         in_slow_timer=1; /* mark this process as the slow timer */
01108         while(1){
01109 #ifdef USE_SIGWAIT
01110                 n=sigwait(&slow_timer_sset, &sig);
01111 #else
01112                 n=sigwaitinfo(&slow_timer_sset, 0);
01113 #endif
01114                 if (n==-1){
01115                         if (errno==EINTR) continue; /* some other signal, ignore it */
01116                         LOG(L_ERR, "ERROR: slow_timer_main: sigwaitinfo failed: %s [%d]\n",
01117                                         strerror(errno), errno);
01118                         sleep(1);
01119                         /* try to continue */
01120                 }
01121 #ifdef USE_SIGWAIT
01122         if (sig!=SLOW_TIMER_SIG){
01123 #ifdef __OS_darwin
01124                 /* on darwin sigwait is buggy: it will cause extreme slow down
01125                    on signal delivery for the signals it doesn't wait on
01126                    (on darwin 8.8.0, g4 1.5Ghz I've measured a 36s delay!).
01127                   To work arround this bug, we sigwait() on all the signals we
01128                   are interested in ser and manually call the master signal handler 
01129                   if the signal!= slow timer signal -- andrei */
01130                 sig_usr(sig);
01131 #endif
01132                 continue;
01133         }
01134 #endif
01135                 /* update the local cfg if needed */
01136                 cfg_update();
01137                 
01138                 LOCK_SLOW_TIMER_LIST();
01139                 while(*s_idx!=*t_idx){
01140                         i= *s_idx%SLOW_LISTS_NO;
01141                         while(slow_timer_lists[i].next!=
01142                                         (struct timer_ln*)&slow_timer_lists[i]){
01143                                 tl=slow_timer_lists[i].next;
01144                                 _timer_rm_list(tl);
01145                                 tl->next=tl->prev=0;
01146 #ifdef TIMER_DEBUG
01147                                 tl->expires_no++;
01148 #endif
01149                                 SET_RUNNING_SLOW(tl);
01150                                 UNLOCK_SLOW_TIMER_LIST();
01151                                         ret=tl->f(*ticks, tl, tl->data);
01152                                         /* reset the configuration group handles */
01153                                         cfg_reset_all();
01154                                         if (ret==0){
01155                                                 /* one shot */
01156                                                 UNSET_RUNNING_SLOW();
01157                                                 LOCK_SLOW_TIMER_LIST();
01158                                         }else{
01159                                                 /* not one shot, re-add it */
01160                                                 LOCK_TIMER_LIST(); /* add it to the "main"  list */
01161                                                         RESET_SLOW_LIST(tl);
01162                                                         if (ret!=(ticks_t)-1) /* != periodic */
01163                                                                 tl->initial_timeout=ret;
01164                                                         _timer_add(*ticks, tl);
01165                                                 UNLOCK_TIMER_LIST();
01166                                                 LOCK_SLOW_TIMER_LIST();
01167                                                 UNSET_RUNNING_SLOW();
01168                                         }
01169                         }
01170                         (*s_idx)++;
01171                 }
01172                 UNLOCK_SLOW_TIMER_LIST();
01173         }
01174         
01175 }
01176 
01177 #endif

Generated on Tue May 22 2012 13:10:17 for SIP Router by  doxygen 1.7.1