/* "main" timer routine * WARNING: it should never be called twice for the same *ticks value * (it could cause too fast expires for long timers), *ticks must be also * always increasing */ static void timer_handler(void) { ticks_t saved_ticks; #ifdef USE_SLOW_TIMER int run_slow_timer; int i; run_slow_timer=0; i=(slow_idx_t)(*t_idx%SLOW_LISTS_NO); #endif /*LM_DBG("called, ticks=%lu, prev_ticks=%lu\n", (unsigned long)*ticks, (unsigned long)prev_ticks); */ run_timer=0; /* reset run_timer */ adjust_ticks(); LOCK_TIMER_LIST(); do{ saved_ticks=*ticks; /* protect against time running backwards */ if (prev_ticks>=saved_ticks){ LM_CRIT("backwards or still time\n"); /* try to continue */ prev_ticks=saved_ticks-1; break; } /* go through all the "missed" ticks, taking a possible overflow * into account */ for (prev_ticks=prev_ticks+1; prev_ticks!=saved_ticks; prev_ticks++) timer_run(prev_ticks); timer_run(prev_ticks); /* do it for saved_ticks too */ }while(saved_ticks!=*ticks); /* in case *ticks changed */ #ifdef USE_SLOW_TIMER timer_list_expire(*ticks, &timer_lst->expired, &slow_timer_lists[i], *t_idx); #else timer_list_expire(*ticks, &timer_lst->expired); #endif /* WARNING: add_timer(...,0) must go directly to expired list, since * otherwise there is a race between timer running and adding it * (it could expire it H0_ENTRIES ticks later instead of 'now')*/ #ifdef USE_SLOW_TIMER if (slow_timer_lists[i].next!=(struct timer_ln*)&slow_timer_lists[i]){ run_slow_timer=1; if ((slow_idx_t)(*t_idx-*s_idx) < (SLOW_LISTS_NO-1U)) (*t_idx)++; else{ LM_WARN("slow timer too slow: overflow (%d - %d = %d)\n", *t_idx, *s_idx, *t_idx-*s_idx); /* trying to continue */ } } #endif UNLOCK_TIMER_LIST(); #ifdef USE_SLOW_TIMER /* wake up the "slow" timer */ if (run_slow_timer) kill(slow_timer_pid, SLOW_TIMER_SIG); #endif }
/* slow timer main function, never exists * This function is intended to be executed in a special separated process * (the "slow" timer) which will run the timer handlers of all the registered * timers not marked as "fast". The ideea is to execute the fast timers in the * "main" timer process, as accurate as possible and defer the execution of the * timers marked as "slow" to the "slow" timer. * Implementation details: * - it waits for a signal and then wakes up and processes * all the lists in slow_timer_lists from [s_idx, t_idx). It will * -it increments *s_idx (at the end it will be == *t_idx) * -all list operations are protected by the "slow" timer lock */ void slow_timer_main() { int n; ticks_t ret; struct timer_ln* tl; unsigned short i; in_slow_timer=1; /* mark this process as the slow timer */ while(1){ #ifdef USE_SIGWAIT n=sigwait(&slow_timer_sset, 0); #else n=sigwaitinfo(&slow_timer_sset, 0); #endif if (n==-1){ if (errno==EINTR) continue; /* some other signal, ignore it */ LOG(L_ERR, "ERROR: slow_timer_main: sigwaitinfo failed: %s [%d]\n", strerror(errno), errno); sleep(1); /* try to continue */ } LOCK_SLOW_TIMER_LIST(); while(*s_idx!=*t_idx){ i= *s_idx%SLOW_LISTS_NO; while(slow_timer_lists[i].next!= (struct timer_ln*)&slow_timer_lists[i]){ tl=slow_timer_lists[i].next; _timer_rm_list(tl); tl->next=tl->prev=0; #ifdef TIMER_DEBUG tl->expires_no++; #endif SET_RUNNING_SLOW(tl); UNLOCK_SLOW_TIMER_LIST(); ret=tl->f(*ticks, tl, tl->data); if (ret==0){ /* one shot */ UNSET_RUNNING_SLOW(); LOCK_SLOW_TIMER_LIST(); }else{ /* not one shot, re-add it */ LOCK_TIMER_LIST(); /* add it to the "main" list */ RESET_SLOW_LIST(tl); if (ret!=(ticks_t)-1) /* != periodic */ tl->initial_timeout=ret; _timer_add(*ticks, tl); UNLOCK_TIMER_LIST(); LOCK_SLOW_TIMER_LIST(); UNSET_RUNNING_SLOW(); } } (*s_idx)++; } UNLOCK_SLOW_TIMER_LIST(); } }
int timer_add_safe(struct timer_ln* tl, ticks_t delta) #endif { int ret; LOCK_TIMER_LIST(); if (tl->flags & F_TIMER_ACTIVE){ #ifdef TIMER_DEBUG LOG(timerlog, "timer_add called on an active timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_add-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_add-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else LM_DBG("timer_add called on an active timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); #endif ret=-1; /* refusing to add active or non-reinit. timer */ goto error; } tl->initial_timeout=delta; if ((tl->next!=0) || (tl->prev!=0)){ LM_CRIT("timer_add: called with linked timer: %p (%p, %p)\n", tl, tl->next, tl->prev); ret=-1; goto error; } tl->flags|=F_TIMER_ACTIVE; #ifdef TIMER_DEBUG tl->add_file=file; tl->add_func=func; tl->add_line=line; tl->add_calls++; #endif ret=_timer_add(*ticks, tl); error: UNLOCK_TIMER_LIST(); return ret; }
/* called from timer_handle, must be called with the timer lock held * WARNING: expired one shot timers are _not_ automatically reinit * (because they could have been already freed from the timer * handler so a reinit would not be safe!) */ inline static void timer_list_expire(ticks_t t, struct timer_head* h #ifdef USE_SLOW_TIMER , struct timer_head* slow_l, slow_idx_t slow_mark #endif ) { struct timer_ln * tl; ticks_t ret; #ifdef TIMER_DEBUG struct timer_ln* first; int i=0; first=h->next; #endif /*LM_DBG("@ ticks = %lu, list =%p\n", (unsigned long) *ticks, h); */ while(h->next!=(struct timer_ln*)h){ tl=h->next; #ifdef TIMER_DEBUG /* FIXME: replace w/ EXTRA_DEBUG */ if (tl==0){ LM_CRIT("timer_list_expire: tl=%p, h=%p {%p, %p}\n", tl, h, h->next, h->prev); abort(); }else if((tl->next==0) || (tl->prev==0)){ LM_CRIT("timer_list_expire: @%d tl=%p " "{ %p, %p, %d, %d, %p, %p, %04x, -}," " h=%p {%p, %p}\n", t, tl, tl->next, tl->prev, tl->expire, tl->initial_timeout, tl->data, tl->f, tl->flags, h, h->next, h->prev); LM_CRIT("-timer_list_expire-: cycle %d, first %p," "running %p\n", i, first, *running_timer); LM_CRIT("-timer_list_expire-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); abort(); } i++; #endif _timer_rm_list(tl); /* detach */ #ifdef USE_SLOW_TIMER if (IS_FAST_TIMER(tl)){ #endif /* if fast timer */ SET_RUNNING(tl); tl->next=tl->prev=0; /* debugging */ #ifdef TIMER_DEBUG tl->expires_no++; #endif UNLOCK_TIMER_LIST(); /* acts also as write barrier */ ret=tl->f(t, tl, tl->data); /* reset the configuration group handles */ cfg_reset_all(); if (ret==0){ UNSET_RUNNING(); LOCK_TIMER_LIST(); }else{ /* not one-shot, re-add it */ LOCK_TIMER_LIST(); if (ret!=(ticks_t)-1) /* ! periodic */ tl->initial_timeout=ret; _timer_add(t, tl); UNSET_RUNNING(); } #ifdef USE_SLOW_TIMER }else{ /* slow timer */ SET_SLOW_LIST(tl); tl->slow_idx=slow_mark; /* current index */ /* overflow check in timer_handler*/ _timer_add_list(slow_l, tl); } #endif } }
int timer_del_safe(struct timer_ln* tl) #endif { int ret; ret=-1; again: /* quick exit if timer inactive */ if ( !(tl->flags & F_TIMER_ACTIVE)){ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del called on an inactive timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("called on an inactive timer %p (%p, %p)," " flags %x\n", tl, tl->next, tl->prev, tl->flags); */ #endif return -1; } #ifdef USE_SLOW_TIMER if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){ LOCK_SLOW_TIMER_LIST(); if (!IS_ON_SLOW_LIST(tl) || (tl->slow_idx==*t_idx)){ UNLOCK_SLOW_TIMER_LIST(); goto again; } if (IS_RUNNING_SLOW(tl)){ UNLOCK_SLOW_TIMER_LIST(); if (IS_IN_TIMER_SLOW()){ /* if somebody tries to shoot himself in the foot, * warn him and ignore the delete */ LM_CRIT("timer handle %p (s) tried to delete" " itself\n", tl); #ifdef TIMER_DEBUG LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #endif return -2; /* do nothing */ } sched_yield(); /* wait for it to complete */ goto again; } if (tl->next!=0){ _timer_rm_list(tl); /* detach */ tl->next=tl->prev=0; ret=0; #ifdef TIMER_DEBUG tl->del_file=file; tl->del_func=func; tl->del_line=line; tl->flags|=F_TIMER_DELETED; #endif }else{ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del: (s) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-: @%d tl=%p " "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), tl, tl->next, tl->prev, tl->expire, tl->initial_timeout, tl->data, tl->f, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("(s) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); */ #endif ret=-1; } UNLOCK_SLOW_TIMER_LIST(); }else{ #endif LOCK_TIMER_LIST(); #ifdef USE_SLOW_TIMER if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){ UNLOCK_TIMER_LIST(); goto again; } #endif if (IS_RUNNING(tl)){ UNLOCK_TIMER_LIST(); if (IS_IN_TIMER()){ /* if somebody tries to shoot himself in the foot, * warn him and ignore the delete */ LM_CRIT("timer handle %p tried to delete" " itself\n", tl); #ifdef TIMER_DEBUG LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #endif return -2; /* do nothing */ } sched_yield(); /* wait for it to complete */ goto again; } if ((tl->next!=0)&&(tl->prev!=0)){ _timer_rm_list(tl); /* detach */ tl->next=tl->prev=0; ret=0; #ifdef TIMER_DEBUG tl->del_file=file; tl->del_func=func; tl->del_line=line; tl->flags|=F_TIMER_DELETED; #endif }else{ #ifdef TIMER_DEBUG LOG(timerlog, "timer_del: (f) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); LOG(timerlog, "WARN: -timer_del-: @%d tl=%p " "{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), tl, tl->next, tl->prev, tl->expire, tl->initial_timeout, tl->data, tl->f, tl->flags); LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n", func, file, line); LOG(timerlog, "WARN: -timer_del-: added %d times" ", last from: %s(%s):%d, deleted %d times" ", last from: %s(%s):%d, init %d times, expired %d \n", tl->add_calls, tl->add_func, tl->add_file, tl->add_line, tl->del_calls, tl->del_func, tl->del_file, tl->del_line, tl->init, tl->expires_no); #else /* LM_DBG("(f) timer %p (%p, %p) flags %x " "already detached\n", tl, tl->next, tl->prev, tl->flags); */ #endif ret=-1; } UNLOCK_TIMER_LIST(); #ifdef USE_SLOW_TIMER } #endif return ret; }
void slow_timer_main() { int n; ticks_t ret; struct timer_ln* tl; unsigned short i; #ifdef USE_SIGWAIT int sig; #endif in_slow_timer=1; /* mark this process as the slow timer */ while(1){ #ifdef USE_SIGWAIT n=sigwait(&slow_timer_sset, &sig); #else n=sigwaitinfo(&slow_timer_sset, 0); #endif if (n==-1){ if (errno==EINTR) continue; /* some other signal, ignore it */ LM_ERR("sigwaitinfo failed: %s [%d]\n", strerror(errno), errno); sleep(1); /* try to continue */ } #ifdef USE_SIGWAIT if (sig!=SLOW_TIMER_SIG){ #ifdef __OS_darwin /* on darwin sigwait is buggy: it will cause extreme slow down * on signal delivery for the signals it doesn't wait on * (on darwin 8.8.0, g4 1.5Ghz I've measured a 36s delay!). * To work arround this bug, we sigwait() on all the signals we * are interested in ser and manually call the master signal handler * if the signal!= slow timer signal -- andrei */ sig_usr(sig); #endif continue; } #endif /* update the local cfg if needed */ cfg_update(); LOCK_SLOW_TIMER_LIST(); while(*s_idx!=*t_idx){ i= *s_idx%SLOW_LISTS_NO; while(slow_timer_lists[i].next!= (struct timer_ln*)&slow_timer_lists[i]){ tl=slow_timer_lists[i].next; _timer_rm_list(tl); tl->next=tl->prev=0; #ifdef TIMER_DEBUG tl->expires_no++; #endif SET_RUNNING_SLOW(tl); UNLOCK_SLOW_TIMER_LIST(); ret=tl->f(*ticks, tl, tl->data); /* reset the configuration group handles */ cfg_reset_all(); if (ret==0){ /* one shot */ UNSET_RUNNING_SLOW(); LOCK_SLOW_TIMER_LIST(); }else{ /* not one shot, re-add it */ LOCK_TIMER_LIST(); /* add it to the "main" list */ RESET_SLOW_LIST(tl); if (ret!=(ticks_t)-1) /* != periodic */ tl->initial_timeout=ret; _timer_add(*ticks, tl); UNLOCK_TIMER_LIST(); LOCK_SLOW_TIMER_LIST(); UNSET_RUNNING_SLOW(); } } (*s_idx)++; } UNLOCK_SLOW_TIMER_LIST(); } }