
/* *******************************************************
/* timers.c - routines to deal with timing out and pausing
/*
/* taken from The Master, Ken Carson
/* re-keyed by Russ Sasnett	6/12/87
/*
/* Externally visible functions are:
/*
/* 	code = set_timeout( interval, flagptr );
/*	int	code;		/* -1 on error */
/*	int	interval;	/* # of millisecs till timeout; 
/*					clear timeout if 0 */
/*	int	*flagptr;	/* 0'd when set, 1'd on timeout condition */
/*				/* 2'd on someone else's timeout condition */
/*
/*	code = mpause(interval);
/*	int	code;		/* -1 on error */
/*	int interval;		/* # of millisecs to wait */

#include <signal.h>
#include <sys/time.h>
#include <stdio.h>

#define	MAXTIMEOUTS	10	/* maximum # of timeouts to track simultaneously */

static	int	timefromnow[MAXTIMEOUTS] = {0,0,0,0,0,0,0,0,0,0};
static	int	*flagptr[MAXTIMEOUTS];
static	int	nexttimeout = -1;


/* ---------------------------------------------------------------------------- */
static
handletimeout()
{
struct itimerval	value;
int			millisecs;
int			i;

#ifdef DEBUG
	printf( "set_timeout: TIMEOUT after %d millisecs, index %d\n",
		timefromnow[nexttimeout],nexttimeout );
#endif

	millisecs = timefromnow[nexttimeout];
	for( i=0; i<MAXTIMEOUTS; i++ )
		if( timefromnow[i]>0 )
		{
			*flagptr[i] = 2;	/* set all timeout flags, till the cause resets */
			timefromnow[i] -= millisecs;
			if( timefromnow[i] == 0 )	/* set those who ran out */
			{
				timefromnow[i] = -1;
				*flagptr[i] = 1;
			}
		}

	nexttimeout = findnexttimeout();
	if( nexttimeout != -1 )
		return (updatetimer(0));
	return (0);
}
/* end handletimeout() */
/* ---------------------------------------------------------------------------- */
static
findnexttimeout()

{
int	next;
int	i;

	next = -1;
	for( i=0; i<MAXTIMEOUTS; i++ )	/* find new nexttimeout */
		if( timefromnow[i]>0 )
			if( next == -1 || timefromnow[i] < timefromnow[next] )
				next = i;

	return (next);
}
/* end findnexttimeout */
/* ---------------------------------------------------------------------------- */
static
getelapsedtime()

{
struct itimerval	value;
int			millisecs;

	if( getitimer( ITIMER_REAL,&value ) == -1 )
	{
		perror( "set_timeout: getitimer");
		return (-1);
	}

	millisecs = value.it_value.tv_sec * 1000;
	millisecs += value.it_value.tv_usec / 1000;	   /* time left till nexttimeout */
	millisecs = timefromnow[nexttimeout] - millisecs;  /* time since nexttimeout set */
	return (millisecs);
}
/* end getelapsedtime */
/* ---------------------------------------------------------------------------- */
static
updatetimer( elapsedtime )

	int	elapsedtime;
{
struct itimerval	value, ovalue;
int			i;

	if( elapsedtime > 0 )
		for( i=0; i<MAXTIMEOUTS; i++ )
			if( timefromnow[i] > 0 )
				timefromnow[i] -= elapsedtime;
	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	value.it_value.tv_sec = timefromnow[nexttimeout]/1000;
	value.it_value.tv_usec = (timefromnow[nexttimeout]%1000)*1000;
	if( setitimer( ITIMER_REAL,&value,&ovalue ) == -1 )
	{
		perror("set_timeout: setting timer");
		return (-1);
	}
	return (0);
}
/* end updatetimer */
/* ---------------------------------------------------------------------------- */
static
timeoutindex( newflagptr )

	int	*newflagptr;
{
int	i;

	for( i=0; i<MAXTIMEOUTS && (timefromnow[i]==0 || flagptr[i]!=newflagptr); i++ );
	if( i==MAXTIMEOUTS )
		for( i=0; i<MAXTIMEOUTS && timefromnow[i]!=0; i++ );
	if( i==MAXTIMEOUTS )	/* not a current flag */
	{
		fprintf( stderr,
			"set_timeout: can't handle additional simultaneous requests\n");
		return (-1);
	}
	return (i);
}
/* end timeoutindex */
/* ---------------------------------------------------------------------------- */
static
clearfinaltimeout()

{
struct itimerval	value, ovalue;
int			rv = 0;

	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	value.it_value.tv_sec = 0;
	value.it_value.tv_usec = 0;
	if( setitimer( ITIMER_REAL,&value,&ovalue) == -1 )
	{
		perror("set_timeout: clearing timer");
		rv = -1;
	}
	if( (int)signal( SIGALRM,SIG_DFL ) == -1 )
	{
		perror("set_timeout: resetting signal handler");
		rv = -1;
	}
	return (rv);
}
/* end clearfinaltimeout */
/* ---------------------------------------------------------------------------- */
set_timeout( interval, newflagptr )

	int	interval;	/* how long till timeout */
	int	*newflagptr;	/* value to set when timeout occurs */
{
int	oldmask;
int	newindex;
int	elapsedtime;
int	i;

	oldmask = sigblock ( 1<<SIGALRM );
	if( (newindex = timeoutindex(newflagptr)) == -1 )
	{
		sigsetmask( oldmask );
		return (-1);
	}

#ifdef DEBUG
	if( interval == 0 )
		printf("set_timeout: clearing timeout, index %d\n",newindex);
	else
		printf("set_timeout: setting timeout for %d millisecs, index %d\n",
			interval,newindex);
#endif

	if( interval <= 0 )	/* request to clear a timeout */
	{
		if( flagptr[newindex] != newflagptr )
		{
			fprintf(stderr,
				"set_timeout: can't clear a timeout that hasn't been set\n");
			sigsetmask( oldmask );
			return (-1);
		}
		if( nexttimeout == -1 )	/* already expired & no more intervals */
		{
			timefromnow[newindex] = 0;
			sigsetmask( oldmask );
			return (clearfinaltimeout());
		}
		if( nexttimeout != newindex )
		{
			timefromnow[newindex] = 0;
			for( i=0; i<MAXTIMEOUTS; i++ )	/* check other flags */
				if( timefromnow[i] > 0 )
					*flagptr[i] = 0;
				else if( timefromnow[i] == -1 )
					for( i=0; i< MAXTIMEOUTS; i++ )
						if( timefromnow[i] > 0 )
							*flagptr[i] = 2;
			sigsetmask( oldmask );
			return (0);
		}
		/* know newindex == nexttimeout */
		elapsedtime = getelapsedtime();
		timefromnow[newindex] = 0;
		nexttimeout = findnexttimeout();
		if( nexttimeout == -1 )	/*this was the last */
		{
			i = clearfinaltimeout();
			sigsetmask( oldmask );
			return (i);
		}
		/* know there are more timeouts, which need to be revalued */
		i = updatetimer( elapsedtime );
		sigsetmask( oldmask );
		return (i);
	}
	/* interval > 0 to get here */
	
	*newflagptr = 0;
	for( i=0; i<MAXTIMEOUTS; i++ )	/* check for a current timeout condition */
		if( timefromnow[i] == -1 )
		{
			*newflagptr = 2;
			break;
		}

	if( nexttimeout == -1 )	/* this is a new & only timeout */
	{
		if( (int)signal(SIGALRM,handletimeout) == -1 )
		{
			perror("set_timeout: setting signal handler");
			sigsetmask( oldmask );
			return (-1);
		}
		nexttimeout = 0;
		flagptr[0] = newflagptr;
		timefromnow[0] = interval;
		i = updatetimer(0);
		sigsetmask( oldmask );
		return (i);
	}

/*	timefromnow[newindex] = 0;
	nexttimeout = findnexttimeout();
*/

	elapsedtime = getelapsedtime();
	if( elapsedtime < 0 )
	{
		sigsetmask( oldmask );
		return (elapsedtime);
	}

	timefromnow[newindex] = interval + elapsedtime;
	flagptr[newindex] = newflagptr;

	if( timefromnow[newindex] >= timefromnow[nexttimeout] )
	{		/* new timeout is not the next timeout */
		sigsetmask( oldmask );
		return (1);
	}

	nexttimeout = newindex;

	i = updatetimer(elapsedtime);
	sigsetmask( oldmask );
	return (i);

}
/* end set_timeout */
/* ---------------------------------------------------------------------------- */
mpause( interval )
	
	int	interval;
{
int	Timeout;

#ifdef DEBUG
	printf("mpause: pausing for %d millisecs\n",interval);
#endif

	if( set_timeout(interval,&Timeout) == -1 )
		return (-1);
/*	sigpause(0);	sometimes hangs with this ... */
	while( !Timeout );
	if( set_timeout(0,&Timeout) == -1 )
		return (-1);
	return (0);
}
/* end mpause */
