/*
httperf -- a tool for measuring web server performance
Copyright (C) 2000 Hewlett-Packard Company
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of httperf, a web server performance measurment
tool.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <httperf.h>
#include <timer.h>
#define WHEEL_SIZE 4096
#if 1
static Time now;
#endif
static Time next_tick;
static Timer *timer_free_list = 0;
static Timer *t_curr = 0;
/* What a wheel is made of, no? */
static Timer_Queue wheel[WHEEL_SIZE], *curr = 0;
static void
done (Timer *t)
{
t->q.next = timer_free_list;
t->q.prev = 0;
timer_free_list = t;
}
Time
timer_now_forced (void)
{
struct timeval tv;
gettimeofday (&tv, 0);
return tv.tv_sec + tv.tv_usec*1e-6;
}
Time
timer_now (void)
{
return now;
}
void
timer_init (void)
{
now = timer_now_forced ();
memset (wheel, 0, sizeof (wheel));
next_tick = timer_now () + TIMER_INTERVAL;
curr = wheel;
}
void
timer_tick (void)
{
Timer *t, *t_next;
assert (!t_curr);
now = timer_now_forced ();
while (timer_now () >= next_tick)
{
for (t = curr->next; t && t->delta == 0; t = t_next)
{
t_curr = t;
(*t->func) (t, t->arg);
t_next = t->q.next;
done (t);
}
t_curr = 0;
curr->next = t;
if (t)
{
t->q.prev = (Timer *) curr;
--t->delta;
}
next_tick += TIMER_INTERVAL;
if (++curr >= wheel + WHEEL_SIZE)
curr = wheel;
}
}
Timer*
timer_schedule (Timer_Callback timeout, Any_Type arg, Time delay)
{
Timer_Queue *spoke;
Timer *t, *p;
u_long ticks;
u_long delta;
Time behind;
if (timer_free_list)
{
t = timer_free_list;
timer_free_list = t->q.next;
}
else
{
t = malloc (sizeof (*t));
if (!t)
{
fprintf (stderr, "%s.timer_schedule: %s\n",
prog_name, strerror (errno));
return 0;
}
}
memset (t, 0, sizeof (*t));
t->func = timeout;
t->arg = arg;
behind = (timer_now () - next_tick);
if (behind > 0.0)
delay += behind;
if (delay < 0.0)
ticks = 1;
else
{
ticks = (delay + TIMER_INTERVAL / 2.0) * (1.0 / TIMER_INTERVAL);
if (!ticks)
ticks = 1; /* minimum delay is a tick */
}
spoke = curr + (ticks % WHEEL_SIZE);
if (spoke >= wheel + WHEEL_SIZE)
spoke -= WHEEL_SIZE;
delta = ticks / WHEEL_SIZE;
p = (Timer *) spoke;
while (p->q.next && delta > p->q.next->delta)
{
delta -= p->q.next->delta;
p = p->q.next;
}
t->q.next = p->q.next;
t->q.prev = p;
p->q.next = t;
t->delta = delta;
if (t->q.next)
{
t->q.next->q.prev = t;
t->q.next->delta -= delta;
}
if (DBG > 2)
fprintf (stderr, "timer_schedule: t=%p, delay=%gs, arg=%lx\n",
t, delay, arg.l);
return t;
}
void
timer_cancel (Timer *t)
{
if (DBG > 2)
fprintf (stderr, "timer_cancel: t=%p\n", t);
assert (t->q.prev);
/* A module MUST NOT call timer_cancel() for a timer that is
currently being processed (whose timeout has expired). */
if (t_curr == t)
{
fprintf (stderr, "timer_cancel() called on currently active timer!\n");
return;
}
if (t->q.next)
{
t->q.next->delta += t->delta;
t->q.next->q.prev = t->q.prev;
}
t->q.prev->q.next = t->q.next;
done (t);
}
syntax highlighted by Code2HTML, v. 0.9.1