/**************************************************************************** * netutils/thttpd/timers.c * Simple Timer Routines * * Copyright (C) 2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Derived from the file of the same name in the original THTTPD package: * * Copyright © 1995,1998,2000 by Jef Poskanzer . * 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 #include #include #include #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(); }