summaryrefslogtreecommitdiff
path: root/apps/netutils/thttpd/timers.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/netutils/thttpd/timers.c')
-rw-r--r--apps/netutils/thttpd/timers.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/apps/netutils/thttpd/timers.c b/apps/netutils/thttpd/timers.c
new file mode 100644
index 000000000..8edd7b21b
--- /dev/null
+++ b/apps/netutils/thttpd/timers.c
@@ -0,0 +1,365 @@
+/****************************************************************************
+ * 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();
+}