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