/* $Header: timer.c,v 1.1 87/03/11 15:59:36 mikew Exp $ */
/* $Source: /afs/andrew.cmu.edu/usr7/kazar/afs/lwp/RCS/timer.c,v $ */

#ifndef lint
static char *rcsid = "$Header: timer.c,v 1.1 87/03/11 15:59:36 mikew Exp $";
#endif

/*
 * P_R_P_Q_# (C) COPYRIGHT IBM CORPORATION 1987
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/*******************************************************************\
* 								    *
* 								    *
* 	Information Technology Center				    *
* 	Carnegie-Mellon University				    *
* 								    *
* 								    *
* 								    *
\*******************************************************************/


#include <sys/time.h>
#define _TIMER_IMPL_
#include "timer.h"

typedef unsigned char bool;
#define FALSE	0
#define TRUE	1

#define NIL	0

#define expiration TotalTime

extern char *malloc();

#define new_elem()	((struct TM_Elem *) malloc(sizeof(struct TM_Elem)))

#define MILLION	1000000

static globalInitDone = 0;

/* t1 = t2 - t3 */

static subtract(t1, t2, t3)
    register struct timeval *t1, *t2, *t3;
{
    register int sec, usec;

    sec = t2 -> tv_sec;
    usec = t2 -> tv_usec;
    if (t3->tv_usec > usec) {
	usec += MILLION;
	sec--;
    }
    t1 -> tv_usec = usec - t3 -> tv_usec;
    t1 -> tv_sec = sec - t3 -> tv_sec;
}

/* t1 += t2; */

static add(t1, t2)
    register struct timeval *t1, *t2;
{
    t1 -> tv_usec += t2 -> tv_usec;
    t1 -> tv_sec += t2 -> tv_sec;
    if (t1->tv_usec >= MILLION) {
	t1 -> tv_sec ++;
	t1 -> tv_usec -= MILLION;
    }
}

/* t1 == t2 */

bool TM_eql(t1, t2)
    register struct timeval *t1, *t2;
{
    return (t1->tv_usec == t2->tv_usec) && (t1->tv_sec == t2->tv_sec);
}

/* t1 >= t2 */

/*
obsolete, commentless procedure, all done by hand expansion now.
static bool geq(t1, t2)
    register struct timeval *t1, *t2;
{
    return (t1->tv_sec > t2->tv_sec) ||
	   (t1->tv_sec == t2->tv_sec && t1->tv_usec >= t2->tv_usec);
}
*/

static bool blocking(t)
    register struct TM_Elem *t;
{
    return (t->TotalTime.tv_sec < 0 || t->TotalTime.tv_usec < 0);
}



/*
    Initializes a list -- returns -1 if failure, else 0.
*/

int TM_Init(list)
    register struct TM_Elem **list;
{
    if (!globalInitDone) {
	FT_Init (0, 0);
	globalInitDone = 1;
    }
    *list = new_elem();
    if (*list == NIL)
	return -1;
    else {
	(*list) -> Next = *list;
	(*list) -> Prev = *list;
	(*list) -> TotalTime.tv_sec = 0;
	(*list) -> TotalTime.tv_usec = 0;
	(*list) -> TimeLeft.tv_sec = 0;
	(*list) -> TimeLeft.tv_usec = 0;
	(*list) -> BackPointer = NIL;

	return 0;
    }
}

int TM_Final(list)
    register struct TM_Elem **list;
{
    if (list == NIL || *list == NIL)
	return -1;
    else {
	free(*list);
	*list = NIL;
	return 0;
    }
}

/*
    Inserts elem into the timer list pointed to by *tlistPtr.
*/

void TM_Insert(tlistPtr, elem)
    struct TM_Elem *tlistPtr;	/* pointer to head pointer of timer list */
    struct TM_Elem *elem;	/* element to be inserted */
{
    extern insque();
    register struct TM_Elem *next;

    /* TimeLeft must be set for function IOMGR with infinite timeouts */
    elem -> TimeLeft = elem -> TotalTime;

    /* Special case -- infinite timeout */
    if (blocking(elem)) {
	insque(elem, tlistPtr->Prev);
	return;
    }

    /* Finite timeout, set expiration time */
    FT_AGetTimeOfDay(&elem->expiration, 0);
    add(&elem->expiration, &elem->TimeLeft);
    next = NIL;
    FOR_ALL_ELTS(p, tlistPtr, {
	if (blocking(p) || !(elem->TimeLeft.tv_sec > p->TimeLeft.tv_sec ||
	    (elem->TimeLeft.tv_sec == p->TimeLeft.tv_sec && elem->TimeLeft.tv_usec >= p->TimeLeft.tv_usec))
	    ) {
		next = p;	/* Save ptr to element that will be after this one */
		break;
	}
     })

    if (next == NIL) next = tlistPtr;
    insque(elem, next->Prev);
}

/*
    Walks through the specified list and updates the TimeLeft fields in it.
    Returns number of expired elements in the list.
*/

int TM_Rescan(tlist)
    struct TM_Elem *tlist;	/* head pointer of timer list */
{
    struct timeval time;
    register int expired;

    FT_AGetTimeOfDay(&time, 0);
    expired = 0;
    FOR_ALL_ELTS(e, tlist, {
	if (!blocking(e)) {
	    subtract(&e->TimeLeft, &e->expiration, &time);
	    if (0 > e->TimeLeft.tv_sec || (0 == e->TimeLeft.tv_sec && 0 >= e->TimeLeft.tv_usec))
		expired++;
	}
    })
    return expired;
}
    
/*
    RETURNS POINTER TO earliest expired entry from tlist.
    Returns 0 if no expired entries are present.
*/

struct TM_Elem *TM_GetExpired(tlist)
    struct TM_Elem *tlist;	/* head pointer of timer list */
{
    FOR_ALL_ELTS(e, tlist, {
	if (!blocking(e) &&
	    (0 > e->TimeLeft.tv_sec || (0 == e->TimeLeft.tv_sec && 0 >= e->TimeLeft.tv_usec)))
		return e;
    })
    return NIL;
}
    
/*
    Returns a pointer to the earliest unexpired element in tlist.
    Its TimeLeft field will specify how much time is left.
    Returns 0 if tlist is empty or if there are no unexpired elements.
*/

struct TM_Elem *TM_GetEarliest(tlist)
    struct TM_Elem *tlist;
{
    register struct TM_Elem *e;

    e = tlist -> Next;
    return (e == tlist ? NIL : e);
}
