/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn <dgk@research.att.com> * * * ***********************************************************************/ #pragma prototyped #include <ast.h> #include <sig.h> #include <error.h> #include "fault.h" #include "defs.h" #include "FEATURE/sigfeatures" #include "FEATURE/time" typedef struct _timer { double wakeup; double incr; struct _timer *next; void (*action)(void*); void *handle; } Timer_t; #define IN_ADDTIMEOUT 1 #define IN_SIGALRM 2 #define DEFER_SIGALRM 4 #define SIGALRM_CALL 8 static Timer_t *tptop, *tpmin, *tpfree; static char time_state; static double getnow(void) { register double now; #ifdef timeofday struct timeval tp; timeofday(&tp); now = tp.tv_sec + 1.e-6*tp.tv_usec; #else now = (double)time((time_t*)0); #endif /* timeofday */ return(now+.001); } /* * set an alarm for <t> seconds */ static double setalarm(register double t) { #if defined(_lib_setitimer) && defined(ITIMER_REAL) struct itimerval tnew, told; tnew.it_value.tv_sec = t; tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec); if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000) tnew.it_value.tv_usec = 1000; tnew.it_interval.tv_sec = 0; tnew.it_interval.tv_usec = 0; if(setitimer(ITIMER_REAL,&tnew,&told) < 0) errormsg(SH_DICT,ERROR_system(1),e_alarm); t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec; #else unsigned seconds = (unsigned)t; if(t && seconds<1) seconds=1; t = (double)alarm(seconds); #endif return(t); } /* signal handler for alarm call */ static void sigalrm(int sig) { register Timer_t *tp, *tplast, *tpold, *tpnext; double now; static double left; NOT_USED(sig); left = 0; if(time_state&SIGALRM_CALL) time_state &= ~SIGALRM_CALL; else if(alarm(0)) sh_fault(SIGALRM|SH_TRAP); if(time_state) { if(time_state&IN_ADDTIMEOUT) time_state |= DEFER_SIGALRM; errno = EINTR; return; } time_state |= IN_SIGALRM; sigrelease(SIGALRM); while(1) { now = getnow(); tpold = tpmin = 0; for(tplast=0,tp=tptop; tp; tp=tpnext) { tpnext = tp->next; if(tp->action) { if(tp->wakeup <=now) { if(!tpold || tpold->wakeup>tp->wakeup) tpold = tp; } else { if(!tpmin || tpmin->wakeup>tp->wakeup) tpmin=tp; } tplast = tp; } else { if(tplast) tplast->next = tp->next; else tptop = tp->next; tp->next = tpfree; tpfree = tp; } } if((tp=tpold) && tp->incr) { while((tp->wakeup += tp->incr) <= now); if(!tpmin || tpmin->wakeup>tp->wakeup) tpmin=tp; } if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left)))) { if(left==0) signal(SIGALRM,sigalrm); left = setalarm(tpmin->wakeup-now); if(left && (now+left) < tpmin->wakeup) setalarm(left); else left=tpmin->wakeup-now; } if(tp) { void (*action)(void*); action = tp->action; if(!tp->incr) tp->action = 0; errno = EINTR; time_state &= ~IN_SIGALRM; (*action)(tp->handle); time_state |= IN_SIGALRM; } else break; } if(!tpmin) signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); time_state &= ~IN_SIGALRM; errno = EINTR; } static void oldalrm(void *handle) { Handler_t fn = *(Handler_t*)handle; free(handle); (*fn)(SIGALRM); } void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle) { register Timer_t *tp; double t; Handler_t fn; t = ((double)msec)/1000.; if(t<=0 || !action) return((void*)0); if(tp=tpfree) tpfree = tp->next; else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t)))) return((void*)0); tp->wakeup = getnow() + t; tp->incr = (flags?t:0); tp->action = action; tp->handle = handle; time_state |= IN_ADDTIMEOUT; tp->next = tptop; tptop = tp; if(!tpmin || tp->wakeup < tpmin->wakeup) { tpmin = tp; fn = (Handler_t)signal(SIGALRM,sigalrm); if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm) { Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t)); if(hp) { *hp = fn; sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp); } } tp = tptop; } else if(tpmin && !tpmin->action) time_state |= DEFER_SIGALRM; time_state &= ~IN_ADDTIMEOUT; if(time_state&DEFER_SIGALRM) { time_state=SIGALRM_CALL; sigalrm(SIGALRM); if(tp!=tptop) tp=0; } return((void*)tp); } /* * delete timer <tp>. If <tp> is NULL, all timers are deleted */ void timerdel(void *handle) { register Timer_t *tp = (Timer_t*)handle; if(tp) tp->action = 0; else { for(tp=tptop; tp; tp=tp->next) tp->action = 0; if(tpmin) { tpmin = 0; setalarm((double)0); } signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); } }