1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 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
getnow(void)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 */
setalarm(register double t)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 */
sigalrm(int sig)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
oldalrm(void * handle)174 static void oldalrm(void *handle)
175 {
176 Handler_t fn = *(Handler_t*)handle;
177 free(handle);
178 (*fn)(SIGALRM);
179 }
180
sh_timeradd(unsigned long msec,int flags,void (* action)(void *),void * handle)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 */
timerdel(void * handle)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