1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2013 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 <dgkorn@gmail.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 */
87 #ifdef _lib_sigaction
sigalrm(int sig,siginfo_t * info,void * context)88 static void sigalrm(int sig, siginfo_t* info, void *context)
89 #else
90 static void sigalrm(int sig)
91 #endif
92 {
93 register Timer_t *tp, *tplast, *tpold, *tpnext;
94 double now;
95 static double left;
96 #ifdef _lib_sigaction
97 Shell_t *shp = sh_getinterp();
98 if(shp->st.trapcom[SIGALRM] && *shp->st.trapcom[SIGALRM])
99 {
100 shp->siginfo[SIGALRM] = malloc(sizeof(siginfo_t));
101 memcpy(shp->siginfo[SIGALRM],context,sizeof(siginfo_t));
102 }
103 #endif
104 NOT_USED(sig);
105 left = 0;
106 if(time_state&SIGALRM_CALL)
107 time_state &= ~SIGALRM_CALL;
108 else if(alarm(0))
109 kill(getpid(),SIGALRM|SH_TRAP);
110 if(time_state)
111 {
112 if(time_state&IN_ADDTIMEOUT)
113 time_state |= DEFER_SIGALRM;
114 errno = EINTR;
115 return;
116 }
117 time_state |= IN_SIGALRM;
118 sigrelease(SIGALRM);
119 while(1)
120 {
121 now = getnow();
122 tpold = tpmin = 0;
123 for(tplast=0,tp=tptop; tp; tp=tpnext)
124 {
125 tpnext = tp->next;
126 if(tp->action)
127 {
128 if(tp->wakeup <=now)
129 {
130 if(!tpold || tpold->wakeup>tp->wakeup)
131 tpold = tp;
132 }
133 else
134 {
135 if(!tpmin || tpmin->wakeup>tp->wakeup)
136 tpmin=tp;
137 }
138 tplast = tp;
139 }
140 else
141 {
142 if(tplast)
143 tplast->next = tp->next;
144 else
145 tptop = tp->next;
146 tp->next = tpfree;
147 tpfree = tp;
148 }
149 }
150 if((tp=tpold) && tp->incr)
151 {
152 while((tp->wakeup += tp->incr) <= now);
153 if(!tpmin || tpmin->wakeup>tp->wakeup)
154 tpmin=tp;
155 }
156 if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left))))
157 {
158 if(left==0)
159 signal(SIGALRM,sigalrm);
160 left = setalarm(tpmin->wakeup-now);
161 if(left && (now+left) < tpmin->wakeup)
162 setalarm(left);
163 else
164 left=tpmin->wakeup-now;
165 }
166 if(tp)
167 {
168 void (*action)(void*);
169 action = tp->action;
170 if(!tp->incr)
171 tp->action = 0;
172 errno = EINTR;
173 time_state &= ~IN_SIGALRM;
174 (*action)(tp->handle);
175 time_state |= IN_SIGALRM;
176 }
177 else
178 break;
179 }
180 if(!tpmin)
181 signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?(sh_sigfun_t)sh_fault:SIG_DFL);
182 time_state &= ~IN_SIGALRM;
183 errno = EINTR;
184 }
185
oldalrm(void * handle)186 static void oldalrm(void *handle)
187 {
188 Handler_t fn = *(Handler_t*)handle;
189 free(handle);
190 (*fn)(SIGALRM);
191 }
192
sh_timeradd(unsigned long msec,int flags,void (* action)(void *),void * handle)193 void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle)
194 {
195 register Timer_t *tp;
196 double t;
197 Handler_t fn;
198 t = ((double)msec)/1000.;
199 if(t<=0 || !action)
200 return((void*)0);
201 if(tp=tpfree)
202 tpfree = tp->next;
203 else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t))))
204 return((void*)0);
205 tp->wakeup = getnow() + t;
206 tp->incr = (flags?t:0);
207 tp->action = action;
208 tp->handle = handle;
209 time_state |= IN_ADDTIMEOUT;
210 tp->next = tptop;
211 tptop = tp;
212 if(!tpmin || tp->wakeup < tpmin->wakeup)
213 {
214 tpmin = tp;
215 fn = (Handler_t)signal(SIGALRM,sigalrm);
216 if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm)
217 {
218 Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t));
219 if(hp)
220 {
221 *hp = fn;
222 sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp);
223 }
224 }
225 tp = tptop;
226 }
227 else if(tpmin && !tpmin->action)
228 time_state |= DEFER_SIGALRM;
229 time_state &= ~IN_ADDTIMEOUT;
230 if(time_state&DEFER_SIGALRM)
231 {
232 time_state=SIGALRM_CALL;
233 kill(getpid(),SIGALRM);
234 if(tp!=tptop)
235 tp=0;
236 }
237 return((void*)tp);
238 }
239
240 /*
241 * delete timer <tp>. If <tp> is NULL, all timers are deleted
242 */
timerdel(void * handle)243 void timerdel(void *handle)
244 {
245 register Timer_t *tp = (Timer_t*)handle;
246 if(tp)
247 tp->action = 0;
248 else
249 {
250 for(tp=tptop; tp; tp=tp->next)
251 tp->action = 0;
252 if(tpmin)
253 {
254 tpmin = 0;
255 setalarm((double)0);
256 }
257 signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?(sh_sigfun_t)sh_fault:SIG_DFL);
258 }
259 }
260
261