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