You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
8.7 KiB
365 lines
8.7 KiB
/**************************************************************************** |
|
* netutils/thttpd/timers.c |
|
* Simple Timer Routines |
|
* |
|
* Copyright (C) 2009 Gregory Nutt. All rights reserved. |
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr> |
|
* |
|
* Derived from the file of the same name in the original THTTPD package: |
|
* |
|
* Copyright © 1995,1998,2000 by Jef Poskanzer <jef@mail.acme.com>. |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
* SUCH DAMAGE. |
|
* |
|
****************************************************************************/ |
|
|
|
/**************************************************************************** |
|
* Included Files |
|
****************************************************************************/ |
|
|
|
#include <sys/time.h> |
|
|
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <debug.h> |
|
|
|
#include "thttpd_alloc.h" |
|
#include "timers.h" |
|
|
|
/**************************************************************************** |
|
* Pre-Processor Definitons |
|
****************************************************************************/ |
|
|
|
#define HASH_SIZE 67 |
|
|
|
/**************************************************************************** |
|
* Private Data |
|
****************************************************************************/ |
|
|
|
static Timer *timers[HASH_SIZE]; |
|
static Timer *free_timers; |
|
|
|
/**************************************************************************** |
|
* Public Data |
|
****************************************************************************/ |
|
|
|
ClientData JunkClientData; |
|
|
|
/**************************************************************************** |
|
* Private Functions |
|
****************************************************************************/ |
|
|
|
static unsigned int hash(Timer *tmr) |
|
{ |
|
/* We can hash on the trigger time, even though it can change over the |
|
* life of a timer via the periodic bit. |
|
* This is because both of those guys call l_resort(), which * recomputes |
|
* the hash and moves the timer to the appropriate list. |
|
*/ |
|
|
|
return ((unsigned int)tmr->time.tv_sec ^ |
|
(unsigned int)tmr->time.tv_usec) % HASH_SIZE; |
|
} |
|
|
|
static void l_add(Timer *tmr) |
|
{ |
|
int h = tmr->hash; |
|
register Timer *tmr2; |
|
register Timer *tmr2prev; |
|
|
|
tmr2 = timers[h]; |
|
if (tmr2 == NULL) |
|
{ |
|
/* The list is empty. */ |
|
timers[h] = tmr; |
|
tmr->prev = tmr->next = NULL; |
|
} |
|
else |
|
{ |
|
if (tmr->time.tv_sec < tmr2->time.tv_sec || |
|
(tmr->time.tv_sec == tmr2->time.tv_sec && |
|
tmr->time.tv_usec <= tmr2->time.tv_usec)) |
|
{ |
|
/* The new timer goes at the head of the list. */ |
|
|
|
timers[h] = tmr; |
|
tmr->prev = NULL; |
|
tmr->next = tmr2; |
|
tmr2->prev = tmr; |
|
} |
|
else |
|
{ |
|
/* Walk the list to find the insertion point. */ |
|
|
|
for (tmr2prev = tmr2, tmr2 = tmr2->next; tmr2 != NULL; |
|
tmr2prev = tmr2, tmr2 = tmr2->next) |
|
{ |
|
if (tmr->time.tv_sec < tmr2->time.tv_sec || |
|
(tmr->time.tv_sec == tmr2->time.tv_sec && |
|
tmr->time.tv_usec <= tmr2->time.tv_usec)) |
|
{ |
|
/* Found it. */ |
|
tmr2prev->next = tmr; |
|
tmr->prev = tmr2prev; |
|
tmr->next = tmr2; |
|
tmr2->prev = tmr; |
|
return; |
|
} |
|
} |
|
|
|
/* Oops, got to the end of the list. Add to tail. */ |
|
|
|
tmr2prev->next = tmr; |
|
tmr->prev = tmr2prev; |
|
tmr->next = NULL; |
|
} |
|
} |
|
} |
|
|
|
static void l_remove(Timer *tmr) |
|
{ |
|
int h = tmr->hash; |
|
|
|
if (tmr->prev == NULL) |
|
{ |
|
timers[h] = tmr->next; |
|
} |
|
else |
|
{ |
|
tmr->prev->next = tmr->next; |
|
} |
|
|
|
if (tmr->next != NULL) |
|
{ |
|
tmr->next->prev = tmr->prev; |
|
} |
|
} |
|
|
|
static void l_resort(Timer *tmr) |
|
{ |
|
/* Remove the timer from its old list. */ |
|
|
|
l_remove(tmr); |
|
|
|
/* Recompute the hash. */ |
|
|
|
tmr->hash = hash(tmr); |
|
|
|
/* And add it back in to its new list, sorted correctly. */ |
|
|
|
l_add(tmr); |
|
} |
|
|
|
/**************************************************************************** |
|
* Public Functions |
|
****************************************************************************/ |
|
|
|
void tmr_init(void) |
|
{ |
|
int h; |
|
|
|
for (h = 0; h < HASH_SIZE; ++h) |
|
{ |
|
timers[h] = NULL; |
|
} |
|
|
|
free_timers = NULL; |
|
} |
|
|
|
Timer *tmr_create(struct timeval *now, TimerProc *timer_proc, |
|
ClientData client_data, long msecs, int periodic) |
|
{ |
|
Timer *tmr; |
|
|
|
if (free_timers != NULL) |
|
{ |
|
tmr = free_timers; |
|
free_timers = tmr->next; |
|
} |
|
else |
|
{ |
|
tmr = (Timer*)httpd_malloc(sizeof(Timer)); |
|
if (!tmr) |
|
{ |
|
return NULL; |
|
} |
|
} |
|
|
|
tmr->timer_proc = timer_proc; |
|
tmr->client_data = client_data; |
|
tmr->msecs = msecs; |
|
tmr->periodic = periodic; |
|
|
|
if (now != NULL) |
|
{ |
|
tmr->time = *now; |
|
} |
|
else |
|
{ |
|
(void)gettimeofday(&tmr->time, NULL); |
|
} |
|
|
|
tmr->time.tv_sec += msecs / 1000L; |
|
tmr->time.tv_usec += (msecs % 1000L) * 1000L; |
|
if (tmr->time.tv_usec >= 1000000L) |
|
{ |
|
tmr->time.tv_sec += tmr->time.tv_usec / 1000000L; |
|
tmr->time.tv_usec %= 1000000L; |
|
} |
|
tmr->hash = hash(tmr); |
|
|
|
/* Add the new timer to the proper active list. */ |
|
|
|
l_add(tmr); |
|
return tmr; |
|
} |
|
|
|
long tmr_mstimeout(struct timeval *now) |
|
{ |
|
int h; |
|
int gotone; |
|
long msecs, m; |
|
register Timer *tmr; |
|
|
|
gotone = 0; |
|
msecs = 0; |
|
|
|
/* Since the lists are sorted, we only need to look at the * first timer on |
|
* each one. |
|
*/ |
|
|
|
for (h = 0; h < HASH_SIZE; ++h) |
|
{ |
|
tmr = timers[h]; |
|
if (tmr != NULL) |
|
{ |
|
m = (tmr->time.tv_sec - now->tv_sec) * 1000L + |
|
(tmr->time.tv_usec - now->tv_usec) / 1000L; |
|
if (!gotone) |
|
{ |
|
msecs = m; |
|
gotone = 1; |
|
} |
|
else if (m < msecs) |
|
{ |
|
msecs = m; |
|
} |
|
} |
|
} |
|
|
|
if (!gotone) |
|
{ |
|
return INFTIM; |
|
} |
|
|
|
if (msecs <= 0) |
|
{ |
|
msecs = 0; |
|
} |
|
|
|
return msecs; |
|
} |
|
|
|
void tmr_run(struct timeval *now) |
|
{ |
|
int h; |
|
Timer *tmr; |
|
Timer *next; |
|
|
|
for (h = 0; h < HASH_SIZE; ++h) |
|
{ |
|
for (tmr = timers[h]; tmr != NULL; tmr = next) |
|
{ |
|
next = tmr->next; |
|
|
|
/* Since the lists are sorted, as soon as we find a timer * that isn'tmr |
|
* ready yet, we can go on to the next list |
|
*/ |
|
|
|
if (tmr->time.tv_sec > now->tv_sec || |
|
(tmr->time.tv_sec == now->tv_sec && tmr->time.tv_usec > now->tv_usec)) |
|
{ |
|
break; |
|
} |
|
|
|
(tmr->timer_proc)(tmr->client_data, now); |
|
if (tmr->periodic) |
|
{ |
|
/* Reschedule. */ |
|
|
|
tmr->time.tv_sec += tmr->msecs / 1000L; |
|
tmr->time.tv_usec += (tmr->msecs % 1000L) * 1000L; |
|
if (tmr->time.tv_usec >= 1000000L) |
|
{ |
|
tmr->time.tv_sec += tmr->time.tv_usec / 1000000L; |
|
tmr->time.tv_usec %= 1000000L; |
|
} |
|
l_resort(tmr); |
|
} |
|
else |
|
{ |
|
tmr_cancel(tmr); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void tmr_cancel(Timer *tmr) |
|
{ |
|
/* Remove it from its active list. */ |
|
|
|
l_remove(tmr); |
|
|
|
/* And put it on the free list. */ |
|
|
|
tmr->next = free_timers; |
|
free_timers = tmr; |
|
tmr->prev = NULL; |
|
} |
|
|
|
void tmr_cleanup(void) |
|
{ |
|
Timer *tmr; |
|
|
|
while (free_timers != NULL) |
|
{ |
|
tmr = free_timers; |
|
free_timers = tmr->next; |
|
httpd_free((void*)tmr); |
|
} |
|
} |
|
|
|
void tmr_destroy(void) |
|
{ |
|
int h; |
|
|
|
for (h = 0; h < HASH_SIZE; ++h) |
|
{ |
|
while (timers[h] != NULL) |
|
{ |
|
tmr_cancel(timers[h]); |
|
} |
|
} |
|
tmr_cleanup(); |
|
}
|
|
|