1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1982-2012 AT&T Intellectual Property * 5* and is licensed under the * 6* Eclipse Public License, Version 1.0 * 7* by AT&T Intellectual Property * 8* * 9* A copy of the License is available at * 10* http://www.eclipse.org/org/documents/epl-v10.html * 11* (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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 30typedef 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 44static Timer_t *tptop, *tpmin, *tpfree; 45static char time_state; 46 47static 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 */ 64static 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 */ 87static 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 kill(getpid(),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 174static void oldalrm(void *handle) 175{ 176 Handler_t fn = *(Handler_t*)handle; 177 free(handle); 178 (*fn)(SIGALRM); 179} 180 181void *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 */ 231void 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