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