Subject: v15i022: Delta time routines for alarm(2) manipulation Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET Submitted-by: Gregg Wonderly Posting-number: Volume 15, Issue 22 Archive-name: timer This shell archive contains the source, man page and test program for some delta time routines. These routines allow you to schedule many things to happen in the future (in terms of a delta time) without having to do all the typical messing with alarm(2). I.E. these routines maintain a list of delta times, routines and corresponding arguments, and then call the routine after the delta time expires, passing the arguement to it. Gregg Wonderly Department of Computing and Information Sciences Oklahoma State University UUCP: {cbosgd, ihnp4, rutgers}!okstate!gregg Internet: gregg@A.CS.OKSTATE.EDU =============================================================================== echo x - README sed '1,$s/^X//' <<\!FUNKY!STUFF! > README XTimer.c contains the source for some delta time manipulation routines. XThese routines allow you to schedule the execution of a routine at Xsome point in the future. ttest.c is a test routine to demonstrate Xsome of the uses of the routines. Compile it using the command, Xmake install, and then run it using the command, ttest. The results Xshould be X X$ make install X cc -O -c timer.c X cc -O -c ttest.c X cc timer.o ttest.o \ X -o xttest X cp xttest ./ttest X$ ttest X1 X*2 X3 X5 X6 X*6 X8 X*10 X*12 X**15 X*16 X**18 X**24 X XThe numbers represent elapsed time in seconds since the command, ttest, Xwas issued. X X- 1.2 - !FUNKY!STUFF! echo x - makefile sed '1,$s/^X//' <<\!FUNKY!STUFF! > makefile X# X# This makefile was generated by MAKEF on Tue Mar 1 16:59:12 1988 X# Version 1.00, updated: Monday January 11, 1988 at 00:33 X# X# 1.2 XLIB = XCFLAGS = -O XLDFLAGS = XBIN = . X# X# This makefile knows how to generate the following program X# XALL_PROGS = xttest X# Xall : $(ALL_PROGS) X# Xinstall : inst-ttest X# X# Install ttest X# Xinst-ttest : ${BIN}/ttest X${BIN}/ttest : xttest X cp xttest ${BIN}/ttest X# X# Stuff to make `TTEST' X# XTTEST_CFILES = timer.c ttest.c XTTEST_OFILES = timer.o ttest.o XTTEST_RPROG = xttest XTTEST_PROG = xttest XTTEST_SYSLIBS = XTTEST_LIBS = X# X$(TTEST_PROG): $(TTEST_LIBS) $(TTEST_OFILES) X cc $(TTEST_OFILES) $(TTEST_LIBS) \ X $(TTEST_SYSLIBS) -o $(TTEST_PROG) X# X# File list to be removed for make clean X# XCLEANUP_FILES = \ X xttest \ X \ X timer.o ttest.o Xclean: X rm -f $(CLEANUP_FILES) X# XALWAYS: X# !FUNKY!STUFF! echo x - timer.1 sed '1,$s/^X//' <<\!FUNKY!STUFF! > timer.1 X.TH TIMER 3L "Oklahoma State University" X.SH NAME Xset_timer, can_timer, did_timer, hold_timer, release_timer \- delta timer X.SH SYNOPSIS X.nf X.PP X.B int set_timer (delta_time, rout, arg) X.B int delta_time; X.B void (*rout)(); X.B char *arg; X.PP X.B int can_timer (rout, arg) X.B int (*rout)(); X.B char *arg; X.PP X.B int did_timer (); X.PP X.B void hold_timer () X.PP X.B void release_timer () X.fi X.SH DESCRIPTION XThese routines manage and provide information about a list of delta time Xactivities that a process has queued for processing. X.PP X.B set_timer Xaccepts a delta time in seconds, which represents the time in the future Xthat X.B rout Xshould be called with X.B arg Xas its only argument. This allows the programmer to queue several events to Xoccur at intervals or in the future. X.B set_timer Xreturns 0 on success and -1 otherwise. X.B errno Xshould convey the reason for failure. X.PP X.B can_timer Xaccepts two arguments which where the second and third arguments to a previous Xcall to X.B set_timer. XThe corresponding entry from the list that X.B set_timer Xplaces requests on, is removed, and will no longer be processed. X.B can_timer Xreturns 0 on success and -1 otherwise. X.B errno Xshould convey the reason for failure. X.PP X.B did_timer Xis a function which returns true if a timer entry has been processed since Xthe last call to X.B did_timer. X.PP X.B hold_timer Xcauses the actual processing of any expired timer entries to be delayed until X.B release_timer Xis called. X.SH BUGS XDue to the granularity of the UNIX clock, some things may actually happen Xat the same time when you expected them to happen 1 second apart. X.SH AUTHOR XGregg Wonderly -- Oklahoma State University X.s X.nf XInternet: gregg@a.cs.okstate.edu XUUCP: {ihnp4, rutgers}!okstate!gregg X.fi X.SH VERSION X- 1.4 - !FUNKY!STUFF! echo x - timer.c sed '1,$s/^X//' <<\!FUNKY!STUFF! > timer.c Xstatic char S_timerc[]= X "@(#)timer.c, 1.4 - Gregg Wonderly@a.cs.okstate.edu - 17:10:52, 3/1/88"; X X/*************************************************************************** X * X * The routines in this file make up a generalized timer facility X * that maintains a list of timer entries, and sorts them according to X * their estimated expire times. Thus calls to set_timer() can be made X * arbitrarily, and the times passed will be the elapsed time from now X * that the timer should go off. set_timer() accepts as arguments the X * delta time from now that the timer should go off, the address of a X * routine to call, and a parameter to pass to that routine. Typically X * this parameter qualifies the timer entry being serviced. X * X * The second routine is called during servicing of the SIGALRM. It X * processes all requests that expire at that time, and then set up the X * next alarm. X * X **************************************************************************/ X X/* System includes. */ X X#include X#include X#include X#include X#include X#include X#include X Xtypedef struct TIMER { X unsigned delta_t; /* Time in seconds till this entry is due. */ X union { X long l_key; X char *p_key; X } u_t; X int (*routine)(); X struct TIMER *next; X} TIMER, *TIMEPTR; X Xstatic TIMEPTR t_queue = NULL, holdq = NULL; Xstatic int hold_timers = 0, done_timer = 0; X Xstatic int alarm_time(); X Xdid_timer() X{ X register int i = done_timer; X X done_timer = 0; X X return (i); X} X X/* X * Process all pending timer entries. X */ X Xrelease_timer () X{ X TIMEPTR p, q; X X hold_timers = 0; X for (p = holdq; p != NULL;) { X if (p->routine != NULL) X (*(p->routine))(p->u_t.p_key); X q = p->next; X free (p); X p = q; X } X holdq = NULL; X} X Xhold_timer () X{ X hold_timers = 1; X} X X/* X * Cancel a timer request by NULLing the routine pointer. Quick and dirty. X */ X Xcan_timer (rout, key) X int (*rout)(); X char *key; X{ X TIMEPTR p; X int left; X X /* Get the time left till the current entry expires. */ X X left = alarm(0); X X /* Correct the first entry to be up to date with elapsed time. */ X X if (t_queue != NULL) X t_queue->delta_t = left; X X /* Find the requested entry, and mark it. */ X X for (p=t_queue; p != NULL; p=p->next) { X if (p->routine == rout && p->u_t.p_key == key) { X p->routine = NULL; X break; X } X } X X if (p == NULL) { X alarm (left); X return (-1); X } X X /* X * Restart the alarm on the next entry so that it doesn't go off until X * necessary. X */ X X for (p=t_queue; p != NULL; p=p->next) { X X /* If we did not nullify the first entry then use it. */ X X if (p->routine != NULL) { X X /* X * If the next available slot happens to be the second or later X * entry in a group that expires at the same time, then we must X * use the value of `left' as the alarm time, NOT the zero value X * in the delta_t slot, which would cause the alarms to be X * canceled X */ X X if (p->delta_t == 0) X alarm (p->delta_t = left); X else X alarm (p->delta_t); X break; X } else { X X /* Otherwise, delete the first entry, and look at the next. */ X X t_queue = p->next; X free (p); X p = t_queue; X } X } X X return (0); X} X X/* X * Set a new timer request to go off after the interval passed expires. X */ X Xset_timer (intv, rout, key) X int intv; X int (*rout)(); X char *key; X{ X TIMEPTR p, prevp = NULL, newp; X unsigned t_left; X X /* Get the remaining time, and put the alarm on hold. */ X X if (t_queue != NULL) X t_left = alarm(0); X else X t_left = 0; X X /* Get a new timer queue entry. */ X X if ((newp = (TIMEPTR) malloc (sizeof (TIMER))) == NULL) X return (-1); X X /* Correct the first entry to be up to date with elapsed time. */ X X if (t_queue != NULL) X t_queue->delta_t = t_left; X X /* X * Search for the insertion point. >= makes the ordering consistant. X * with the order of the calls to set_timer (). X */ X X for (p=t_queue; p != NULL && intv >= p->delta_t; p = p->next) { X prevp = p; X intv -= p->delta_t; X } X X /* Is this the first entry, or insertion at end of list? */ X X if (p == NULL) { X X /* If first entry, then put it in. */ X X if (prevp == NULL) { X t_queue = newp; X X /* If last, then just insert the entry. */ X X } else X prevp->next = newp; X X newp->next = NULL; X X /* If insertion at beginning or in middle. */ X X } else { X X /* If insertion at beginning then, put in entry, and redo alarm. */ X X if (prevp == NULL) { X newp->next = t_queue; X t_queue = newp; X X /* Otherwise, insertion in the middle, so don't touch alarm time. */ X X } else { X newp->next = p; X prevp->next = newp; X } X p->delta_t -= intv; X } X X newp->delta_t = intv; X newp->u_t.p_key = key; X newp->routine = rout; X X /* Reset the alarm to go off later. */ X X signal (SIGALRM, alarm_time); X alarm (t_queue->delta_t); X X return (0); X} X X/* X * Called when an alarm goes off, picks off the first timer entry, queues X * the next one, and then processes the one picked off. X */ X Xstatic int alarm_time (sig) X unsigned sig; X{ X TIMEPTR p; X X done_timer = 1; X signal (SIGALRM, alarm_time); X X do { X p = t_queue; X if ((t_queue = t_queue->next) != NULL && (t_queue->delta_t > 0)) { X X /* Set the next alarm time. */ X X alarm (t_queue->delta_t); X } X X /* If holding timer entries, then place this one on the list. */ X X if (hold_timers) { X p->next = holdq; X holdq = p; X } else { X X /* Invoke the routine requested. */ X X if (p->routine != NULL) X (*(p->routine))(p->u_t.p_key); X X /* Free the member. */ X X free (p); X } X } while (t_queue != NULL && t_queue->delta_t == 0); X X /* And back out. */ X} !FUNKY!STUFF! echo x - ttest.c sed '1,$s/^X//' <<\!FUNKY!STUFF! > ttest.c Xstatic char S_ttestc[]= X "@(#)ttest.c, 1.1 - Gregg Wonderly@a.cs.okstate.edu - 16:55:50, 3/1/88"; X X#include X Xextern exit(), sub1(), sub2(), sub3(), catch(); X Xmain (argc, argv) X int argc; X char **argv; X{ X X if (set_timer (8, sub1, (char *)8)) { X perror ("set_timer"); X exit (1); X } X if (set_timer (1, sub1, (char *)1)) { X perror ("set_timer"); X exit (1); X } X if (set_timer (3, sub1, (char *)3)) { X perror ("set_timer"); X exit (1); X } X if (set_timer (6, sub1, (char *)6)) { X perror ("set_timer"); X exit (1); X } X if (set_timer (5, sub1, (char *)5)) { X perror ("set_timer"); X exit (1); X } X X if (set_timer (26, exit, (char *)0)) { X perror ("set_timer"); X exit (1); X } X X signal (SIGINT, catch); X while (1) { X pause (); X if (!did_timer()) X perror ("pause"); X if (did_timer()) X perror ("set_timer"); X } X} X Xsub1(i) X char *i; X{ X printf ("%d\n", (int)i); X set_timer ((int)i, sub2, (char *) ((int)i*2)); X} X Xsub2 (i) X char *i; X{ X printf ("*%d\n", (int)i); X set_timer ((int)i/2, sub3, (char *)((int)i+((int)i/2))); X if ((int)i < 10) X can_timer (sub3, (char *)((int)i+((int)i/2))); X} X Xsub3(i) X char *i; X{ X printf ("**%d\n", (int)i); X} X Xcatch (i) X int i; X{ X signal (i, catch); X} !FUNKY!STUFF!