xref: /titanic_50/usr/src/lib/libshell/common/sh/timers.c (revision 275c9da86e89f8abf71135cf63d9fc23671b2e60)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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 
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 
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  */
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 static 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 		sh_fault(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 
174 static void oldalrm(void *handle)
175 {
176 	Handler_t fn = *(Handler_t*)handle;
177 	free(handle);
178 	(*fn)(SIGALRM);
179 }
180 
181 void *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  */
231 void	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