/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
*
wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
* ----------------------------------------------------------------------------
*
* Simple timer queue management
*
* $Id: timer.c,v 1.2 2002/07/04 08:47:25 j Exp $
*/
#include
#include
#include
#include "timer.h"
struct timerq
{
struct timerq *next;
time_t when;
timeout_t action;
union timeoutarg arg;
};
static struct timerq *tqhead;
static time_t now;
void
timertick(void)
{
struct timerq *p, *q;
now++;
q = p = 0;
/*
* First, find the cutoff entry. Everything from current tqhead up
* to that entry needs to be triggered afterwards. Adjust tqhead
* accordingly, so it points onto the first entry that needs to be
* triggered in future.
*/
while (tqhead)
{
if (now < tqhead->when)
/* no more actions by now */
break;
if (!q)
/* first event to trigger now, remember it */
q = tqhead;
p = tqhead;
tqhead = tqhead->next;
}
if (!q)
/* nothing to be done */
return;
/*
* Now, tqhead points to the new queue (that is to be considered
* next time). The previous head of the queue, up to the cutoff
* entry, contains timers that are expired now, and their callback
* functions needs to be run. It is important that the queue
* starting at tqhead is stable again, since the callback functions
* are expected to possibly insert new timers as well.
*
* Mark the tail of this subchain, then walk through it, call each
* callback function in succession, and free up that entry.
*/
p->next = 0;
for (p = q; p;)
{
p->action(p->arg);
q = p;
p = p->next;
free(q);
}
}
void *
timeout(time_t tm, timeout_t fun, union timeoutarg arg)
{
struct timerq *p, *q, *newent;
if ((newent = malloc(sizeof(struct timerq))) == 0)
return 0;
newent->action = fun;
newent->arg = arg;
tm += now;
newent->when = tm;
/*
* If queue is currently empty, just put this element onto top of
* queue, and we are done.
*/
if (tqhead == 0)
{
tqhead = newent;
newent->next = 0;
return newent;
}
/*
* If there are existing entries, find the position with the first
* element that is to be triggered after us. Always remember our
* predecessor, so we can adjust their `next' field after queue
* insertion. If there was no prececessor, we've got a new tqhead
* instead.
*/
for (p = tqhead, q = 0; p; q = p, p = p->next)
{
if (tm < p->when)
{
/*
* Insert before current entry, update previous in chain (or
* head of queue).
*/
newent->next = p;
if (q)
q->next = newent;
else
tqhead = newent;
return newent;
}
}
/*
* Fell off the end of the queue: must append new entry to tail,
* and update previous tail.
*/
q->next = newent;
newent->next = 0;
return newent;
}
/*
* Just try looking up `handle' in the queue, and remove it. Again,
* fix up the `next' field of our predecessor, or missing one, fix up
* tqhead instead.
*/
int
untimeout(void *handle)
{
struct timerq *ev = handle, *p, *q;
for (p = tqhead, q = 0; p; q = p, p = p->next)
{
if (p == ev)
{
if (q)
q->next = p->next;
else
tqhead = p->next;
free(p);
return 0;
}
}
return -1;
}