Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

timer.c

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

Generated on Thu Sep 9 04:15:55 2010 for SIPExpressRouter by  doxygen 1.3.9.1