timer.c
cd57180a
 /*
  * $Id$
7dd0b342
  *
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
08aaf730
  * This file is part of SIP-router, a free SIP server.
7dd0b342
  *
08aaf730
  * SIP-router is free software; you can redistribute it and/or modify
7dd0b342
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
08aaf730
  * SIP-router is distributed in the hope that it will be useful,
7dd0b342
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
cd57180a
  */
e3dccdc9
 /* History:
  * --------
  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
3c8bd369
  *  2003-03-29  cleaning pkg_mallocs introduced (jiri)
af93cbdf
  *  2005-07-27  complete re-design/re-implementation (andrei)
17cde665
  *  2005-12-12  workaround & bug reporting for timer_del(self) called from
0f283a68
  *              a timer handle; added timer_allow_del()  (andrei)
17cde665
  *  2007-05-26  workaround for darwin sigwait() bug, see slow_timer_main() or
  *              grep __OS_darwin for more info (andrei)
7fc49b59
  *  2007-07-01  timer_del() returns <0 if the timer is not active or 
  *               cannot be deleted (andrei)
e3dccdc9
  */
cd57180a
 
7dd0b342
 
08aaf730
 /**
  * @file
  * @brief SIP-router core :: 
  * @ingroup core
  * Module: @ref core
1d0661db
  */
 
cd57180a
 #include "timer.h"
af93cbdf
 #include "timer_funcs.h"
 #include "timer_ticks.h"
cd57180a
 #include "dprint.h"
af93cbdf
 #include <time.h>     /* gettimeofday */
 #include <sys/time.h> /* setitimer, gettimeofday */
 #include <signal.h>   /* SIGALRM */
 #include <errno.h>
 #include <unistd.h> /* pause() */
 #include <stdlib.h> /* random, debugging only */
cd57180a
 #include "error.h"
af93cbdf
 #include "signals.h"
 /*
cd57180a
 #include "config.h"
af93cbdf
 */
 #include "globals.h"
dda9dab1
 #include "mem/mem.h"
d4f2d8b0
 #ifdef SHM_MEM
dda9dab1
 #include "mem/shm_mem.h"
d4f2d8b0
 #endif
af93cbdf
 #include "locking.h"
a83e2612
 #include "sched_yield.h"
9188021a
 #include "cfg/cfg_struct.h"
af93cbdf
 
cd57180a
 
af93cbdf
 /* how often will the timer handler be called (in ticks) */
 #define TIMER_HANDLER_INTERVAL	1U
 /* how often to try to re-adjust the ticks */
 #define TIMER_RESYNC_TICKS	(TIMER_TICKS_HZ*5U)  /* each 5 s */
 #define TIMER_MAX_DRIFT	(TIMER_TICKS_HZ/10U) /* if drift > 0.1s adjust */
cd57180a
 
 
 
af93cbdf
 static ticks_t* ticks=0;
 static ticks_t last_ticks; /* last time we adjusted the time */
 static ticks_t last_adj_check; /* last time we ran adjust_ticks */
 static ticks_t prev_ticks; /* last time we ran the timer, also used as
 							  "current" ticks when running the timer for
 							  "skipped" ticks */
 
 static struct timeval last_time;
 static struct timeval start_time; /* for debugging */
 
 static volatile int run_timer=0;
cd57180a
 static int timer_id=0;
 
af93cbdf
 static gen_lock_t* timer_lock=0;
 static struct timer_ln* volatile* running_timer=0;/* running timer handler */
0f283a68
 static int in_timer=0;
 
 #define IS_IN_TIMER() (in_timer)
af93cbdf
 
 #define LOCK_TIMER_LIST()		lock_get(timer_lock)
 #define UNLOCK_TIMER_LIST()		lock_release(timer_lock)
 
 /* we can get away without atomic_set/atomic_cmp and write barriers because we
  * always call SET_RUNNING and IS_RUNNING while holding the timer lock
  * => it's implicitly atomic and the lock acts as write barrier */
 #define SET_RUNNING(t)		(*running_timer=(t))
 #define IS_RUNNING(t)		(*running_timer==(t))
 #define UNSET_RUNNING()		(*running_timer=0)
 
 #ifdef USE_SLOW_TIMER
 
 #define SLOW_TIMER_SIG	SIGUSR2
 /* timer flags checks */
 #define IS_FAST_TIMER(t)	(t->flags&F_TIMER_FAST)
 #define SET_SLOW_LIST(t)	(t->flags|=F_TIMER_ON_SLOW_LIST)
 #define RESET_SLOW_LIST(t)	(t->flags&=~F_TIMER_ON_SLOW_LIST)
 #define IS_ON_SLOW_LIST(t)	(t->flags&F_TIMER_ON_SLOW_LIST)
 
 #define SLOW_LISTS_NO	1024U  /* slow lists number, 2^k recommended */
 
 
 static gen_lock_t*  slow_timer_lock; /* slow timer lock */
 static struct timer_head* slow_timer_lists; 
 static volatile unsigned short* t_idx; /* "main" timer index in slow_lists[] */
 static volatile unsigned short* s_idx; /* "slow" timer index in slow_lists[] */
 static struct timer_ln* volatile* running_timer2=0; /* timer handler running
 													     in the "slow" timer */
 static sigset_t slow_timer_sset;
 pid_t slow_timer_pid;
0f283a68
 static int in_slow_timer=0;
 
 #define IS_IN_TIMER_SLOW() (in_slow_timer)
af93cbdf
 #define SET_RUNNING_SLOW(t)		(*running_timer2=(t))
 #define IS_RUNNING_SLOW(t)		(*running_timer2==(t))
 #define UNSET_RUNNING_SLOW()	(*running_timer2=0)
 
 #define LOCK_SLOW_TIMER_LIST()		lock_get(slow_timer_lock)
 #define UNLOCK_SLOW_TIMER_LIST()	lock_release(slow_timer_lock)
 
 
 #endif
 
 
 struct timer_lists* timer_lst=0;
 
 void sig_timer(int signo)
 {
 	(*ticks)++;
 	if (( *ticks % TIMER_HANDLER_INTERVAL)==0){
 		/* set a flag to run the handler */
 		run_timer=1;
 	}
 }
 
 
 
 void destroy_timer()
 {
 	struct itimerval it;
 	
 	/* disable timer */
 	memset(&it, 0, sizeof(it));
 	setitimer(ITIMER_REAL, &it, 0); 
 	set_sig_h(SIGALRM, SIG_IGN);
 	if (timer_lock){
 		lock_destroy(timer_lock);
 		lock_dealloc(timer_lock);
 		timer_lock=0;
 	}
 	if (ticks){
 #ifdef SHM_MEM
 		shm_free(ticks);
 #else
 		pkg_free(ticks);
 #endif
 		ticks=0;
 	}
 	if (timer_lst){
 #ifdef SHM_MEM
 		shm_free(timer_lst);
 #else
 		pkg_free(timer_lst);
 #endif
 		timer_lst=0;
 	}
 	if (running_timer){
 		shm_free((void*)running_timer);
 		running_timer=0;
 	}
 #ifdef USE_SLOW_TIMER
 	if (slow_timer_lock){
 		lock_destroy(slow_timer_lock);
 		lock_dealloc(slow_timer_lock);
 		slow_timer_lock=0;
 	}
 	if (slow_timer_lists){
 		shm_free((void*)slow_timer_lists);
 		slow_timer_lists=0;
 	}
 	if (t_idx){
 		shm_free((void*)t_idx);
 		t_idx=0;
 	}
 	if (s_idx){
 		shm_free((void*)s_idx);
 		s_idx=0;
 	}
 	if(running_timer2){
 		shm_free((void*)running_timer2);
 		running_timer2=0;
 	}
 #endif
 }
 
d4f2d8b0
 
 
 /* ret 0 on success, <0 on error*/
 int init_timer()
 {
af93cbdf
 	int r;
 	int ret;
 	
 	ret=-1;
 	
 	/* init the locks */
 	timer_lock=lock_alloc();
 	if (timer_lock==0){
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	if (lock_init(timer_lock)==0){
 		lock_dealloc(timer_lock);
 		timer_lock=0;
 		ret=-1;
 		goto error;
 	}
 	/* init the shared structs */
d4f2d8b0
 #ifdef SHM_MEM
af93cbdf
 	ticks=shm_malloc(sizeof(ticks_t));
 	timer_lst=shm_malloc(sizeof(struct timer_lists));
d4f2d8b0
 #else
 	/* in this case get_ticks won't work! */
 	LOG(L_INFO, "WARNING: no shared memory support compiled in"
 				" get_ticks won't work\n");
af93cbdf
 	ticks=pkg_malloc(sizeof(ticks_t));
 	timer_lst=pkg_malloc(sizeof(struct timer_lists));
d4f2d8b0
 #endif
af93cbdf
 	if (ticks==0){
 		LOG(L_CRIT, "ERROR: init_timer: out of shared memory (ticks)\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
d4f2d8b0
 	}
af93cbdf
 	if (timer_lst==0){
 		LOG(L_CRIT, "ERROR: init_timer: out of shared memory (timer_lst)\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	running_timer=shm_malloc(sizeof(struct timer_ln*));
 	if (running_timer==0){
 		LOG(L_CRIT, "ERROR: init_timer: out of memory (running_timer)\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 
 	/* initial values */
 	memset(timer_lst, 0, sizeof(struct timer_lists));
 	*ticks=random(); /* random value for start, for debugging */
 	prev_ticks=last_ticks=last_adj_check=*ticks;
 	*running_timer=0;
 	if (gettimeofday(&start_time, 0)<0){
 		LOG(L_ERR, "ERROR: init_timer: gettimeofday failed: %s [%d]\n",
 				strerror(errno), errno);
 		ret=-1;
 		goto error;
 	}
 	last_time=start_time;
 	DBG("init_timer: starting with *ticks=%u\n", (unsigned) *ticks);
 	
 	/* init timer structures */
 	for (r=0; r<H0_ENTRIES; r++)
 		_timer_init_list(&timer_lst->h0[r]);
 	for (r=0; r<H1_ENTRIES; r++)
 		_timer_init_list(&timer_lst->h1[r]);
 	for (r=0; r<H2_ENTRIES; r++)
 		_timer_init_list(&timer_lst->h2[r]);
 	_timer_init_list(&timer_lst->expired);
 	
 #ifdef USE_SLOW_TIMER
 	
 	/* init the locks */
 	slow_timer_lock=lock_alloc();
 	if (slow_timer_lock==0){
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	if (lock_init(slow_timer_lock)==0){
 		lock_dealloc(slow_timer_lock);
 		slow_timer_lock=0;
 		ret=-1;
 		goto error;
 	}
 	t_idx=shm_malloc(sizeof(*t_idx));
 	s_idx=shm_malloc(sizeof(*s_idx));
 	slow_timer_lists=shm_malloc(sizeof(struct timer_head)*SLOW_LISTS_NO);
 	running_timer2=shm_malloc(sizeof(struct timer_ln*));
 	if ((t_idx==0)||(s_idx==0) || (slow_timer_lists==0) ||(running_timer2==0)){
 		LOG(L_ERR, "ERROR: init_timer: out of shared memory (slow)\n");
 		ret=E_OUT_OF_MEM;
 		goto error;
 	}
 	*t_idx=*s_idx=0;
 	*running_timer2=0;
 	for (r=0; r<SLOW_LISTS_NO; r++)
 		_timer_init_list(&slow_timer_lists[r]);
 	
 #endif
 	
 	DBG("init_timer: timer_list between %p and %p\n",
 			&timer_lst->h0[0], &timer_lst->h2[H2_ENTRIES]);
d4f2d8b0
 	return 0;
af93cbdf
 error:
 	destroy_timer();
 	return ret;
d4f2d8b0
 }
 
 
 
af93cbdf
 #ifdef USE_SLOW_TIMER
 /* arm the "slow" timer ( start it) 
  * returns -1 on error
  * WARNING: use it in the same process as the timer
  *  (the one using pause(); timer_handler()) or
  *  change run_timer to a pointer in shared mem */
 int arm_slow_timer()
8aeb47e2
 {
af93cbdf
 	sigemptyset(&slow_timer_sset);
 	sigaddset(&slow_timer_sset, SLOW_TIMER_SIG);
 again:
 	if (sigprocmask(SIG_BLOCK, &slow_timer_sset, 0)==-1){
 		if (errno==EINTR) goto again;
 		LOG(L_ERR, "ERROR: arm_slow_timer: sigprocmask failed: %s [%d]}n",
 				strerror(errno), errno);
 		goto error;
 	}
17cde665
 #ifdef __OS_darwin
 	/* workaround for darwin sigwait bug, see slow_timer_main() for more
 	   info (or grep __OS_darwin) */
 	/* keep in sync wih main.c: sig_usr() - signals we are interested in */
 	sigaddset(&slow_timer_sset, SIGINT);
 	sigaddset(&slow_timer_sset, SIGTERM);
 	sigaddset(&slow_timer_sset, SIGUSR1);
 	sigaddset(&slow_timer_sset, SIGHUP);
 	sigaddset(&slow_timer_sset, SIGCHLD);
 	sigaddset(&slow_timer_sset, SIGALRM);
 #endif
9188021a
 	/* initialize the config framework */
 	if (cfg_child_init()) goto error;
 
af93cbdf
 	return 0;
 error:
 	return -1;
 }
 #endif
3c8bd369
 
af93cbdf
 
 
 
 /* arm the timer ( start it) 
  * returns -1 on error
  * WARNING: use it in the same process as the timer
  *  (the one using pause(); timer_handler()) or
  *  change run_timer to a pointer in shared mem */
 int arm_timer()
 {
 	struct itimerval it;
 	/* init signal generation */
 	it.it_interval.tv_sec=0;
 	it.it_interval.tv_usec=1000000/TIMER_TICKS_HZ;
 	it.it_value=it.it_interval;
 	/* install the signal handler */
 	if (set_sig_h(SIGALRM, sig_timer) == SIG_ERR ){
 		LOG(L_CRIT, "ERROR: init_timer: the SIGALRM signal handler cannot"
 					" be installed: %s [%d]\n", strerror(errno), errno);
 		return -1;
 	}
 	if (setitimer(ITIMER_REAL, &it, 0) == -1){
 		LOG(L_CRIT, "ERROR: init_timer: setitimer failed: %s [%d]\n",
 					strerror(errno), errno);
 		return -1;
 	}
 	if (gettimeofday(&last_time, 0)<0){
 		LOG(L_ERR, "ERROR: arm_timer: gettimeofday failed: %s [%d]\n",
 				strerror(errno), errno);
 		return -1;
 	}
9188021a
 	/* initialize the config framework */
 	if (cfg_child_init()) return -1;
 
af93cbdf
 	return 0;
 }
 
 
 
9b089e21
 #ifdef DBG_ser_time
 /* debugging  only */
 void check_ser_drift();
 #endif /* DBG_set_time */
 
 
 
af93cbdf
 /* adjust the timer using the "real" time, each TIMER_RESYNC_TICKS, but only
  * if timer drift > TIMER_MAX_DRIFT
  * NOTES: - it will adjust time within  TIMER_MAX_DRIFT from the "real"
  *          elapsed time
  *        - it will never decrease the *ticks, only increase it (monotonic)
  *        - it works ok as long as the adjustment interval < MAX_TICKS_T
  * -- andrei
  */
b264d2c6
 inline static void adjust_ticks(void)
af93cbdf
 {
 	struct timeval crt_time;
 	long long diff_time;
 	ticks_t diff_time_ticks;
 	ticks_t diff_ticks_raw;
 	s_ticks_t delta;
 	
 	/* fix ticks if necessary */
 	if ((*ticks-last_adj_check)>=(ticks_t)TIMER_RESYNC_TICKS){
9b089e21
 #ifdef DBG_ser_time
 		check_ser_drift();
 #endif /* DBG_ser_time */
af93cbdf
 		last_adj_check=*ticks;
 		if (gettimeofday(&crt_time, 0)<0){
 			LOG(L_ERR, "ERROR: adjust_ticks: gettimeofday failed: %s [%d]\n",
 				strerror(errno), errno);
 			return; /* ignore */
 		}
 		diff_time=(long long)crt_time.tv_sec*1000000+crt_time.tv_usec-
 					((long long) last_time.tv_sec*1000000+last_time.tv_usec);
 		if (diff_time<0){
 			LOG(L_WARN, "WARNING: time changed backwards %ld ms ignoring...\n",
 						(long)(diff_time/1000));
 			last_time=crt_time;
 			last_ticks=*ticks;
 		}else{
 			diff_ticks_raw=*ticks-last_ticks;
 			diff_time_ticks=(ticks_t)((diff_time*TIMER_TICKS_HZ)/1000000LL);
 			delta=(s_ticks_t)(diff_time_ticks-diff_ticks_raw);
 			if (delta<-1){
 				LOG(L_WARN, "WARNING: our timer runs faster then real-time"
0aa3fcd6
 						" (%lu ms / %u ticks our time .->"
 						 " %lu ms / %u ticks real time)\n", 
 						(unsigned long)(diff_ticks_raw*1000L/TIMER_TICKS_HZ),
 						diff_ticks_raw,
 						(unsigned long)(diff_time/1000), diff_time_ticks);
af93cbdf
 				last_time=crt_time;
 				last_ticks=*ticks;
 			}else{
 				/* fix the ticks */
 				if (delta>(s_ticks_t)TIMER_MAX_DRIFT){
 #ifndef TIMER_DEBUG
af3f6ba6
 					if (delta > 2*(s_ticks_t)TIMER_MAX_DRIFT+1)
af93cbdf
 #endif
0f3a6b9d
 						DBG("adjusting timer ticks (%lu) with %ld ms"
af93cbdf
 								" (%ld ticks)\n",
 								(unsigned long)*ticks,
 							(long)(delta*1000)/TIMER_TICKS_HZ, (long)delta);
 					*ticks+=(ticks_t)delta;
 				}else{
 					/*DBG("incredible, but our timer is in sync with"
 							" real time (%lu)\n", (unsigned long)*ticks);
 					*/
 				}
 			}
 		}
 	}
 }
 
 
 
9b089e21
 /* time(2) equivalent, using ser internal timers (faster then a syscall) */
 time_t ser_time(time_t *t)
 {
 	if (likely(t==0))
 		return last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
 	*t=last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
 	return *t;
 }
 
 
 
 /* gettimeofday(2) equivalent, using ser internal timers (faster 
  * but more imprecise)
  * WARNING: ignores tz (it's obsolete anyway)*/
 int ser_gettimeofday(struct timeval* tv, struct timezone* tz)
 {
 	if (likely(tv!=0)){
 		tv->tv_sec=last_time.tv_sec+TICKS_TO_S(*ticks-last_ticks);
 		tv->tv_usec=last_time.tv_usec+
 					(TICKS_TO_MS(*ticks-last_ticks)%1000)*1000;
 	}
 	return 0;
 }
 
 
 
 #ifdef DBG_ser_time
 /* debugging  only, remove */
 void check_ser_drift()
 {
 	time_t t1, t2;
 	struct timeval tv1, tv2;
 	int r;
 	
 	t1=time(0);
 	t2=ser_time(0);
 	if (t1!=t2)
 		BUG("time(0)!=ser_time(0) : %d != %d \n", (unsigned)t1, (unsigned)t2);
 	
 	r=gettimeofday(&tv1, 0);
 	ser_gettimeofday(&tv2, 0);
 	if (tv1.tv_sec!=tv2.tv_sec)
 		BUG("gettimeofday seconds!=ser_gettimeofday seconds : %d != %d \n",
 				(unsigned)tv1.tv_sec, (unsigned)tv2.tv_sec);
 	else if ((tv1.tv_usec > tv2.tv_usec) && 
 				(unsigned)(tv1.tv_usec-tv2.tv_usec)>100000)
 		BUG("gettimeofday usecs > ser_gettimeofday with > 0.1s : %d ms\n",
 			(unsigned)(tv1.tv_usec-tv2.tv_usec)/1000);
 	else if ((tv1.tv_usec < tv2.tv_usec) && 
 				(unsigned)(tv2.tv_usec-tv1.tv_usec)>100000)
 		BUG("gettimeofday usecs < ser_gettimeofday with > 0.1s : %d ms\n",
 			(unsigned)(tv2.tv_usec-tv1.tv_usec)/1000);
 }
 #endif /* DBG_ser_time */
 
 
af93cbdf
 
 struct timer_ln* timer_alloc()
 {
 	return shm_malloc(sizeof(struct timer_ln));
 }
 
 void timer_free(struct timer_ln* t)
 {
 	shm_free(t);
 }
 
 
 /* unsafe (no lock ) timer add function
  * t = current ticks
  * tl must be filled (the intial_timeout and flags must be set)
  * returns -1 on error, 0 on success */
 static inline int _timer_add(ticks_t t, struct timer_ln* tl)
 {
 	ticks_t delta;
 
 #ifdef USE_SLOW_TIMER
 	tl->flags&=~((unsigned short)F_TIMER_ON_SLOW_LIST);
 	tl->slow_idx=0;
 #endif
 	delta=tl->initial_timeout;
 	tl->expire=t+delta;
 	return _timer_dist_tl(tl, delta);
 }
 
 
 
 /* "public", safe timer add functions
  * adds a timer at delta ticks from the current time
  * returns -1 on error, 0 on success
7fc49b59
  * WARNING: to re-add an expired or deleted timer you must call
af93cbdf
  *          timer_reinit(tl) prior to timer_add
  *          The default behaviour allows timer_add to add a timer only if it
7fc49b59
  *          has never been added before.
  */
af93cbdf
 #ifdef TIMER_DEBUG
 int timer_add_safe(struct timer_ln* tl, ticks_t delta,
 					const char* file, const char* func, unsigned line)
8aeb47e2
 #else
af93cbdf
 int timer_add_safe(struct timer_ln* tl, ticks_t delta)
8aeb47e2
 #endif
af93cbdf
 {
 	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
 		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;
8aeb47e2
 	}
af93cbdf
 	tl->initial_timeout=delta;
 	if ((tl->next!=0) || (tl->prev!=0)){
 		LOG(L_CRIT, "BUG: 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;
 }
 
 
3c8bd369
 
af93cbdf
 /* safe timer delete
  * deletes tl and inits the list pointer to 0
7fc49b59
  * returns  <0 on error (-1 if timer not active/already deleted and -2 if 
  *           delete attempted from the timer handler) and 0 on success
af93cbdf
  */
 #ifdef TIMER_DEBUG
7fc49b59
 int timer_del_safe(struct timer_ln* tl,
af93cbdf
 					const char* file, const char* func, unsigned line)
 #else
7fc49b59
 int timer_del_safe(struct timer_ln* tl)
af93cbdf
 #endif
 {
7fc49b59
 	int ret;
af93cbdf
 	
7fc49b59
 	ret=-1;
af93cbdf
 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
7fc49b59
 /*
af93cbdf
 		DBG("timer_del called on an inactive timer %p (%p, %p),"
 					" flags %x\n", tl, tl->next, tl->prev, tl->flags);
7fc49b59
 */
af93cbdf
 #endif
7fc49b59
 		return -1;
af93cbdf
 	}
 #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();
0f283a68
 				if (IS_IN_TIMER_SLOW()){
 					/* if somebody tries to shoot himself in the foot,
 					 * warn him and ignore the delete */
 					LOG(L_CRIT, "BUG: 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
7fc49b59
 					return -2; /* do nothing */
0f283a68
 				}
af93cbdf
 				sched_yield(); /* wait for it to complete */
 				goto again;
 			}
 			if (tl->next!=0){
 				_timer_rm_list(tl); /* detach */
 				tl->next=tl->prev=0;
7fc49b59
 				ret=0;
af93cbdf
 #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
7fc49b59
 /*
af93cbdf
 				DBG("timer_del: (s) timer %p (%p, %p) flags %x "
 							"already detached\n",
 							tl, tl->next, tl->prev, tl->flags);
7fc49b59
 */
af93cbdf
 #endif
7fc49b59
 				ret=-1;
af93cbdf
 			}
 			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();
0f283a68
 				if (IS_IN_TIMER()){
 					/* if somebody tries to shoot himself in the foot,
 					 * warn him and ignore the delete */
 					LOG(L_CRIT, "BUG: 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
7fc49b59
 					return -2; /* do nothing */
0f283a68
 				}
af93cbdf
 				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;
7fc49b59
 				ret=0;
af93cbdf
 #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);
0f283a68
 				LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
af93cbdf
 						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
7fc49b59
 /*
af93cbdf
 				DBG("timer_del: (f) timer %p (%p, %p) flags %x "
 							"already detached\n",
 							tl, tl->next, tl->prev, tl->flags);
7fc49b59
 */
af93cbdf
 #endif
7fc49b59
 				ret=-1;
af93cbdf
 			}
 			UNLOCK_TIMER_LIST();
 #ifdef USE_SLOW_TIMER
 		}
 #endif
7fc49b59
 return ret;
af93cbdf
 }
 
 
 
0f283a68
 /* marks a timer as "to be deleted when the handler ends", usefull when
  * the timer handler knows it won't prolong the timer anymore (it will 
  * return 0) and will do some time consuming work. Calling this function
  * will cause simultaneous timer_dels to return immediately (they won't 
  * wait anymore for the timer handle to finish). It will also allow 
  * self-deleting from the timer handle without bug reports.
  * WARNING: - if you rely on timer_del to know when the timer handle execution
  *            finishes (e.g. to free resources used in the timer handle), don't
  *            use this function.
  *          - this function can be called only from a timer handle (in timer
  *            context), all other calls will have no effect and will log a
  *            bug message
  */
23bd471b
 void timer_allow_del(void)
0f283a68
 {
 	if (IS_IN_TIMER() ){
 			UNSET_RUNNING();
 	}else
 #ifdef USE_SLOW_TIMER
 	if (IS_IN_TIMER_SLOW()){
 			UNSET_RUNNING_SLOW();
 	}else 
 #endif
 		LOG(L_CRIT, "BUG: timer_allow_del called outside a timer handle\n");
 }
 
 
7fc49b59
 /* 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!) */
af93cbdf
 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
 	
 	/*DBG("timer_list_expire @ 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){
 			LOG(L_CRIT, "BUG: 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)){
 			LOG(L_CRIT, "BUG: 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);
 			LOG(L_CRIT, "BUG: -timer_list_expire-: cycle %d, first %p,"
 						"running %p\n", i, first, *running_timer);
 			LOG(L_CRIT, "BUG: -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);
58829230
 				/* reset the configuration group handles */
 				cfg_reset_all();
af93cbdf
 				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
3c8bd369
 	}
8aeb47e2
 }
 
 
 
af93cbdf
 /* "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 */
b264d2c6
 static void timer_handler(void)
af93cbdf
 {
 	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
 	
 	/*DBG("timer_handler: 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){
 			LOG(L_CRIT, "BUG: timer_handler: 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{
566c30dd
 			LOG(L_WARN, "slow timer too slow: overflow (%d - %d = %d)\n",
af93cbdf
 					*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
 }
 
 
 
 /* main timer function, never exists */
 void timer_main()
 {
0f283a68
 	in_timer=1; /* mark this process as the fast timer */
af93cbdf
 	while(1){
 		if (run_timer){
9188021a
 			/* update the local cfg if needed */
 			cfg_update();
 
af93cbdf
 			timer_handler();
 		}
 		pause();
 	}
 }
 
 
 
 /* generic call back for the old style timer functions */
 static ticks_t compat_old_handler(ticks_t ti, struct timer_ln* tl,
 									void * data)
 {
 	struct sr_timer* t;
 	
05bb04c7
 #ifdef TIMER_DEBUG
af93cbdf
 	DBG("timer: compat_old_handler: calling, ticks=%u/%u, tl=%p, t=%p\n",
 			prev_ticks, (unsigned)*ticks, tl, data);
05bb04c7
 #endif
af93cbdf
 	t=(struct sr_timer*)data;
 	t->timer_f(TICKS_TO_S(*ticks), t->t_param);
 	return (ticks_t)-1; /* periodic */
 }
 
 
 
 /* register a periodic timer;
  * compatibility mode.w/ the old timer interface...
20581c5a
  * ret: <0 on error
  * Hint: if you need it in a module, register it from mod_init or it 
  * won't work otherwise*/
cd57180a
 int register_timer(timer_function f, void* param, unsigned int interval)
 {
 	struct sr_timer* t;
 
af93cbdf
 	t=shm_malloc(sizeof(struct sr_timer));
cd57180a
 	if (t==0){
 		LOG(L_ERR, "ERROR: register_timer: out of memory\n");
 		goto error;
 	}
 	t->id=timer_id++;
 	t->timer_f=f;
 	t->t_param=param;
af93cbdf
 	
 	timer_init(&t->tl, compat_old_handler, t, 0); /* is slow */
 	if (timer_add(&t->tl, S_TO_TICKS(interval))!=0){
 		LOG(L_ERR, "ERROR: register_timer: timer_add failed\n");
 		return -1;
 	}
 	
cd57180a
 	return t->id;
 
 error:
 	return E_OUT_OF_MEM;
 }
 
 
 
af93cbdf
 ticks_t get_ticks_raw()
cd57180a
 {
af93cbdf
 #ifndef SHM_MEM
 	LOG(L_CRIT, "WARNING: get_ticks: no shared memory support compiled in"
 			", returning 0 (probably wrong)");
 	return 0;
 #endif
 	return *ticks;
cd57180a
 }
b902c38b
 
 
 
af93cbdf
 /* returns tick in s (for compatibility with the old code) */
 ticks_t get_ticks()
b902c38b
 {
d4f2d8b0
 #ifndef SHM_MEM
 	LOG(L_CRIT, "WARNING: get_ticks: no shared memory support compiled in"
 			", returning 0 (probably wrong)");
 	return 0;
 #endif
af93cbdf
 	return TICKS_TO_S(*ticks);
 }
 
 
 #ifdef USE_SLOW_TIMER
 
 
 /* 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
  */
17cde665
 #ifdef __OS_darwin
 extern void sig_usr(int signo);
 #endif
 
af93cbdf
 void slow_timer_main()
 {
 	int n;
 	ticks_t ret;
 	struct timer_ln* tl;
 	unsigned short i;
17cde665
 #ifdef USE_SIGWAIT
 	int sig;
 #endif
af93cbdf
 	
0f283a68
 	in_slow_timer=1; /* mark this process as the slow timer */
af93cbdf
 	while(1){
000f4d33
 #ifdef USE_SIGWAIT
17cde665
 		n=sigwait(&slow_timer_sset, &sig);
000f4d33
 #else
af93cbdf
 		n=sigwaitinfo(&slow_timer_sset, 0);
000f4d33
 #endif
af93cbdf
 		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 */
 		}
17cde665
 #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
9188021a
 		/* update the local cfg if needed */
 		cfg_update();
 		
af93cbdf
 		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);
58829230
 					/* reset the configuration group handles */
 					cfg_reset_all();
af93cbdf
 					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();
 	}
 	
b902c38b
 }
af93cbdf
 
 #endif