summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ksh93/sh/timers.c')
-rw-r--r--src/cmd/ksh93/sh/timers.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/timers.c b/src/cmd/ksh93/sh/timers.c
new file mode 100644
index 0000000..d9ace52
--- /dev/null
+++ b/src/cmd/ksh93/sh/timers.c
@@ -0,0 +1,248 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+#include <ast.h>
+#include <sig.h>
+#include <error.h>
+#include "fault.h"
+#include "defs.h"
+#include "FEATURE/sigfeatures"
+#include "FEATURE/time"
+
+typedef struct _timer
+{
+ double wakeup;
+ double incr;
+ struct _timer *next;
+ void (*action)(void*);
+ void *handle;
+} Timer_t;
+
+#define IN_ADDTIMEOUT 1
+#define IN_SIGALRM 2
+#define DEFER_SIGALRM 4
+#define SIGALRM_CALL 8
+
+static Timer_t *tptop, *tpmin, *tpfree;
+static char time_state;
+
+static double getnow(void)
+{
+ register double now;
+#ifdef timeofday
+ struct timeval tp;
+ timeofday(&tp);
+ now = tp.tv_sec + 1.e-6*tp.tv_usec;
+
+#else
+ now = (double)time((time_t*)0);
+#endif /* timeofday */
+ return(now+.001);
+}
+
+/*
+ * set an alarm for <t> seconds
+ */
+static double setalarm(register double t)
+{
+#if defined(_lib_setitimer) && defined(ITIMER_REAL)
+ struct itimerval tnew, told;
+ tnew.it_value.tv_sec = t;
+ tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec);
+ if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000)
+ tnew.it_value.tv_usec = 1000;
+ tnew.it_interval.tv_sec = 0;
+ tnew.it_interval.tv_usec = 0;
+ if(setitimer(ITIMER_REAL,&tnew,&told) < 0)
+ errormsg(SH_DICT,ERROR_system(1),e_alarm);
+ t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec;
+#else
+ unsigned seconds = (unsigned)t;
+ if(t && seconds<1)
+ seconds=1;
+ t = (double)alarm(seconds);
+#endif
+ return(t);
+}
+
+/* signal handler for alarm call */
+static void sigalrm(int sig)
+{
+ register Timer_t *tp, *tplast, *tpold, *tpnext;
+ double now;
+ static double left;
+ NOT_USED(sig);
+ left = 0;
+ if(time_state&SIGALRM_CALL)
+ time_state &= ~SIGALRM_CALL;
+ else if(alarm(0))
+ sh_fault(SIGALRM|SH_TRAP);
+ if(time_state)
+ {
+ if(time_state&IN_ADDTIMEOUT)
+ time_state |= DEFER_SIGALRM;
+ errno = EINTR;
+ return;
+ }
+ time_state |= IN_SIGALRM;
+ sigrelease(SIGALRM);
+ while(1)
+ {
+ now = getnow();
+ tpold = tpmin = 0;
+ for(tplast=0,tp=tptop; tp; tp=tpnext)
+ {
+ tpnext = tp->next;
+ if(tp->action)
+ {
+ if(tp->wakeup <=now)
+ {
+ if(!tpold || tpold->wakeup>tp->wakeup)
+ tpold = tp;
+ }
+ else
+ {
+ if(!tpmin || tpmin->wakeup>tp->wakeup)
+ tpmin=tp;
+ }
+ tplast = tp;
+ }
+ else
+ {
+ if(tplast)
+ tplast->next = tp->next;
+ else
+ tptop = tp->next;
+ tp->next = tpfree;
+ tpfree = tp;
+ }
+ }
+ if((tp=tpold) && tp->incr)
+ {
+ while((tp->wakeup += tp->incr) <= now);
+ if(!tpmin || tpmin->wakeup>tp->wakeup)
+ tpmin=tp;
+ }
+ if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left))))
+ {
+ if(left==0)
+ signal(SIGALRM,sigalrm);
+ left = setalarm(tpmin->wakeup-now);
+ if(left && (now+left) < tpmin->wakeup)
+ setalarm(left);
+ else
+ left=tpmin->wakeup-now;
+ }
+ if(tp)
+ {
+ void (*action)(void*);
+ action = tp->action;
+ if(!tp->incr)
+ tp->action = 0;
+ errno = EINTR;
+ time_state &= ~IN_SIGALRM;
+ (*action)(tp->handle);
+ time_state |= IN_SIGALRM;
+ }
+ else
+ break;
+ }
+ if(!tpmin)
+ signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
+ time_state &= ~IN_SIGALRM;
+ errno = EINTR;
+}
+
+static void oldalrm(void *handle)
+{
+ Handler_t fn = *(Handler_t*)handle;
+ free(handle);
+ (*fn)(SIGALRM);
+}
+
+void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle)
+{
+ register Timer_t *tp;
+ double t;
+ Handler_t fn;
+ t = ((double)msec)/1000.;
+ if(t<=0 || !action)
+ return((void*)0);
+ if(tp=tpfree)
+ tpfree = tp->next;
+ else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t))))
+ return((void*)0);
+ tp->wakeup = getnow() + t;
+ tp->incr = (flags?t:0);
+ tp->action = action;
+ tp->handle = handle;
+ time_state |= IN_ADDTIMEOUT;
+ tp->next = tptop;
+ tptop = tp;
+ if(!tpmin || tp->wakeup < tpmin->wakeup)
+ {
+ tpmin = tp;
+ fn = (Handler_t)signal(SIGALRM,sigalrm);
+ if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm)
+ {
+ Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t));
+ if(hp)
+ {
+ *hp = fn;
+ sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp);
+ }
+ }
+ tp = tptop;
+ }
+ else if(tpmin && !tpmin->action)
+ time_state |= DEFER_SIGALRM;
+ time_state &= ~IN_ADDTIMEOUT;
+ if(time_state&DEFER_SIGALRM)
+ {
+ time_state=SIGALRM_CALL;
+ sigalrm(SIGALRM);
+ if(tp!=tptop)
+ tp=0;
+ }
+ return((void*)tp);
+}
+
+/*
+ * delete timer <tp>. If <tp> is NULL, all timers are deleted
+ */
+void timerdel(void *handle)
+{
+ register Timer_t *tp = (Timer_t*)handle;
+ if(tp)
+ tp->action = 0;
+ else
+ {
+ for(tp=tptop; tp; tp=tp->next)
+ tp->action = 0;
+ if(tpmin)
+ {
+ tpmin = 0;
+ setalarm((double)0);
+ }
+ signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
+ }
+}
+