| /**************************************************************************** |
| * apps/netutils/thttpd/timers.c |
| * Simple Timer Routines |
| * |
| * Copyright (C) 2009 Gregory Nutt. All rights reserved. |
| * Author: Gregory Nutt <gnutt@nuttx.org> |
| * |
| * 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 Definitions |
| ****************************************************************************/ |
| |
| #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 |
| { |
| 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; |
| long 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't 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(); |
| } |