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