v15i022: Delta time routines for alarm(2) manipulation

Rich Salz rsalz at uunet.uu.net
Wed Jun 1 06:03:43 AEST 1988


Submitted-by: Gregg Wonderly <gregg at a.cs.okstate.edu>
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 at 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 at 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 at 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	<stdio.h>
X#include	<errno.h>
X#include	<sys/types.h>
X#include	<sys/ipc.h>
X#include	<sys/msg.h>
X#include	<signal.h>
X#include	<setjmp.h>
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 at a.cs.okstate.edu  -  16:55:50, 3/1/88";
X
X#include <signal.h>
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!
-- 
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.



More information about the Comp.sources.unix mailing list