1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2009 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * David Korn <dgk@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 22 #include <ast.h> 23 #include <sig.h> 24 #include <error.h> 25 #include "fault.h" 26 #include "defs.h" 27 #include "FEATURE/sigfeatures" 28 #include "FEATURE/time" 29 30 typedef struct _timer 31 { 32 double wakeup; 33 double incr; 34 struct _timer *next; 35 void (*action)(void*); 36 void *handle; 37 } Timer_t; 38 39 #define IN_ADDTIMEOUT 1 40 #define IN_SIGALRM 2 41 #define DEFER_SIGALRM 4 42 #define SIGALRM_CALL 8 43 44 static Timer_t *tptop, *tpmin, *tpfree; 45 static char time_state; 46 47 static double getnow(void) 48 { 49 register double now; 50 #ifdef timeofday 51 struct timeval tp; 52 timeofday(&tp); 53 now = tp.tv_sec + 1.e-6*tp.tv_usec; 54 55 #else 56 now = (double)time((time_t*)0); 57 #endif /* timeofday */ 58 return(now+.001); 59 } 60 61 /* 62 * set an alarm for <t> seconds 63 */ 64 static double setalarm(register double t) 65 { 66 #if defined(_lib_setitimer) && defined(ITIMER_REAL) 67 struct itimerval tnew, told; 68 tnew.it_value.tv_sec = t; 69 tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec); 70 if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000) 71 tnew.it_value.tv_usec = 1000; 72 tnew.it_interval.tv_sec = 0; 73 tnew.it_interval.tv_usec = 0; 74 if(setitimer(ITIMER_REAL,&tnew,&told) < 0) 75 errormsg(SH_DICT,ERROR_system(1),e_alarm); 76 t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec; 77 #else 78 unsigned seconds = (unsigned)t; 79 if(t && seconds<1) 80 seconds=1; 81 t = (double)alarm(seconds); 82 #endif 83 return(t); 84 } 85 86 /* signal handler for alarm call */ 87 static void sigalrm(int sig) 88 { 89 register Timer_t *tp, *tplast, *tpold, *tpnext; 90 double now; 91 static double left; 92 NOT_USED(sig); 93 left = 0; 94 if(time_state&SIGALRM_CALL) 95 time_state &= ~SIGALRM_CALL; 96 else if(alarm(0)) 97 sh_fault(SIGALRM|SH_TRAP); 98 if(time_state) 99 { 100 if(time_state&IN_ADDTIMEOUT) 101 time_state |= DEFER_SIGALRM; 102 errno = EINTR; 103 return; 104 } 105 time_state |= IN_SIGALRM; 106 sigrelease(SIGALRM); 107 while(1) 108 { 109 now = getnow(); 110 tpold = tpmin = 0; 111 for(tplast=0,tp=tptop; tp; tp=tpnext) 112 { 113 tpnext = tp->next; 114 if(tp->action) 115 { 116 if(tp->wakeup <=now) 117 { 118 if(!tpold || tpold->wakeup>tp->wakeup) 119 tpold = tp; 120 } 121 else 122 { 123 if(!tpmin || tpmin->wakeup>tp->wakeup) 124 tpmin=tp; 125 } 126 tplast = tp; 127 } 128 else 129 { 130 if(tplast) 131 tplast->next = tp->next; 132 else 133 tptop = tp->next; 134 tp->next = tpfree; 135 tpfree = tp; 136 } 137 } 138 if((tp=tpold) && tp->incr) 139 { 140 while((tp->wakeup += tp->incr) <= now); 141 if(!tpmin || tpmin->wakeup>tp->wakeup) 142 tpmin=tp; 143 } 144 if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left)))) 145 { 146 if(left==0) 147 signal(SIGALRM,sigalrm); 148 left = setalarm(tpmin->wakeup-now); 149 if(left && (now+left) < tpmin->wakeup) 150 setalarm(left); 151 else 152 left=tpmin->wakeup-now; 153 } 154 if(tp) 155 { 156 void (*action)(void*); 157 action = tp->action; 158 if(!tp->incr) 159 tp->action = 0; 160 errno = EINTR; 161 time_state &= ~IN_SIGALRM; 162 (*action)(tp->handle); 163 time_state |= IN_SIGALRM; 164 } 165 else 166 break; 167 } 168 if(!tpmin) 169 signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); 170 time_state &= ~IN_SIGALRM; 171 errno = EINTR; 172 } 173 174 static void oldalrm(void *handle) 175 { 176 Handler_t fn = *(Handler_t*)handle; 177 free(handle); 178 (*fn)(SIGALRM); 179 } 180 181 void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle) 182 { 183 register Timer_t *tp; 184 double t; 185 Handler_t fn; 186 t = ((double)msec)/1000.; 187 if(t<=0 || !action) 188 return((void*)0); 189 if(tp=tpfree) 190 tpfree = tp->next; 191 else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t)))) 192 return((void*)0); 193 tp->wakeup = getnow() + t; 194 tp->incr = (flags?t:0); 195 tp->action = action; 196 tp->handle = handle; 197 time_state |= IN_ADDTIMEOUT; 198 tp->next = tptop; 199 tptop = tp; 200 if(!tpmin || tp->wakeup < tpmin->wakeup) 201 { 202 tpmin = tp; 203 fn = (Handler_t)signal(SIGALRM,sigalrm); 204 if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm) 205 { 206 Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t)); 207 if(hp) 208 { 209 *hp = fn; 210 sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp); 211 } 212 } 213 tp = tptop; 214 } 215 else if(tpmin && !tpmin->action) 216 time_state |= DEFER_SIGALRM; 217 time_state &= ~IN_ADDTIMEOUT; 218 if(time_state&DEFER_SIGALRM) 219 { 220 time_state=SIGALRM_CALL; 221 sigalrm(SIGALRM); 222 if(tp!=tptop) 223 tp=0; 224 } 225 return((void*)tp); 226 } 227 228 /* 229 * delete timer <tp>. If <tp> is NULL, all timers are deleted 230 */ 231 void timerdel(void *handle) 232 { 233 register Timer_t *tp = (Timer_t*)handle; 234 if(tp) 235 tp->action = 0; 236 else 237 { 238 for(tp=tptop; tp; tp=tp->next) 239 tp->action = 0; 240 if(tpmin) 241 { 242 tpmin = 0; 243 setalarm((double)0); 244 } 245 signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); 246 } 247 } 248 249