chiark / gitweb /
Bring CHANGES.html up to date
[disorder] / lib / event.c
index 3618bf41c7b4e4e9cd853339f597361f512f1694..ee25024c594369c8818471dd6b2b07a5a2ed778f 100644 (file)
@@ -1,27 +1,25 @@
 /*
  * This file is part of DisOrder.
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2007 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2007, 2008 Richard Kettlewell
  *
  *
- * This program is free software; you can redistribute it and/or modify
+ * 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
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  * (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.
- *
+ * 
+ * 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
  * 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
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 /** @file lib/event.c
  * @brief DisOrder event loop
  */
 
  */
 /** @file lib/event.c
  * @brief DisOrder event loop
  */
 
-#include <config.h>
+#include "common.h"
 
 #include <unistd.h>
 #include <fcntl.h>
 
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <assert.h>
 #include <signal.h>
 #include <errno.h>
 #include <signal.h>
 #include <errno.h>
-#include <string.h>
-#include <limits.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/un.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/un.h>
-#include <stdio.h>
 #include "event.h"
 #include "mem.h"
 #include "log.h"
 #include "event.h"
 #include "mem.h"
 #include "log.h"
@@ -47,6 +41,8 @@
 #include "printf.h"
 #include "sink.h"
 #include "vector.h"
 #include "printf.h"
 #include "sink.h"
 #include "vector.h"
+#include "timeval.h"
+#include "heap.h"
 
 /** @brief A timeout */
 struct timeout {
 
 /** @brief A timeout */
 struct timeout {
@@ -54,9 +50,18 @@ struct timeout {
   struct timeval when;
   ev_timeout_callback *callback;
   void *u;
   struct timeval when;
   ev_timeout_callback *callback;
   void *u;
-  int resolve;
+  int active;
 };
 
 };
 
+/** @brief Comparison function for timeouts */
+static int timeout_lt(const struct timeout *a,
+                     const struct timeout *b) {
+  return tvlt(&a->when, &b->when);
+}
+
+HEAP_TYPE(timeout_heap, struct timeout *, timeout_lt);
+HEAP_DEFINE(timeout_heap, struct timeout *, timeout_lt);
+
 /** @brief A file descriptor in one mode */
 struct fd {
   int fd;
 /** @brief A file descriptor in one mode */
 struct fd {
   int fd;
@@ -106,11 +111,8 @@ struct ev_source {
   /** @brief File descriptors, per mode */
   struct fdmode mode[ev_nmodes];
 
   /** @brief File descriptors, per mode */
   struct fdmode mode[ev_nmodes];
 
-  /** @brief Sorted linked list of timeouts
-   *
-   * We could use @ref HEAP_TYPE now, but there aren't many timeouts.
-   */
-  struct timeout *timeouts;
+  /** @brief Heap of timeouts */
+  struct timeout_heap timeouts[1];
 
   /** @brief Array of handled signals */
   struct signal signals[NSIG];
 
   /** @brief Array of handled signals */
   struct signal signals[NSIG];
@@ -146,27 +148,6 @@ static const char *modenames[] = { "read", "write", "except" };
 
 /* utilities ******************************************************************/
 
 
 /* utilities ******************************************************************/
 
-/** @brief Great-than comparison for timevals
- *
- * Ought to be in @file lib/timeval.h
- */
-static inline int gt(const struct timeval *a, const struct timeval *b) {
-  if(a->tv_sec > b->tv_sec)
-    return 1;
-  if(a->tv_sec == b->tv_sec
-     && a->tv_usec > b->tv_usec)
-    return 1;
-  return 0;
-}
-
-/** @brief Greater-than-or-equal comparison for timevals
- *
- * Ought to be in @ref lib/timeval.h
- */
-static inline int ge(const struct timeval *a, const struct timeval *b) {
-  return !gt(b, a);
-}
-
 /* creation *******************************************************************/
 
 /** @brief Create a new event loop */
 /* creation *******************************************************************/
 
 /** @brief Create a new event loop */
@@ -179,6 +160,7 @@ ev_source *ev_new(void) {
     FD_ZERO(&ev->mode[n].enabled);
   ev->sigpipe[0] = ev->sigpipe[1] = -1;
   sigemptyset(&ev->sigmask);
     FD_ZERO(&ev->mode[n].enabled);
   ev->sigpipe[0] = ev->sigpipe[1] = -1;
   sigemptyset(&ev->sigmask);
+  timeout_heap_init(ev->timeouts);
   return ev;
 }
 
   return ev;
 }
 
@@ -194,7 +176,7 @@ int ev_run(ev_source *ev) {
     int n, mode;
     int ret;
     int maxfd;
     int n, mode;
     int ret;
     int maxfd;
-    struct timeout *t, **tt;
+    struct timeout *timeouts, *t, **tt;
     struct stat sb;
 
     xgettimeofday(&now, 0);
     struct stat sb;
 
     xgettimeofday(&now, 0);
@@ -202,10 +184,24 @@ int ev_run(ev_source *ev) {
      * while we're handling them (otherwise we'd have to break out of infinite
      * loops, preferrably without starving better-behaved subsystems).  Hence
      * the slightly complicated two-phase approach here. */
      * while we're handling them (otherwise we'd have to break out of infinite
      * loops, preferrably without starving better-behaved subsystems).  Hence
      * the slightly complicated two-phase approach here. */
-    for(t = ev->timeouts;
-       t && ge(&now, &t->when);
-       t = t->next) {
-      t->resolve = 1;
+    /* First we read those timeouts that have triggered out of the heap.  We
+     * keep them in the same order they came out of the heap in. */
+    tt = &timeouts;
+    while(timeout_heap_count(ev->timeouts)
+         && tvle(&timeout_heap_first(ev->timeouts)->when, &now)) {
+      /* This timeout has reached its trigger time; provided it has not been
+       * cancelled we add it to the timeouts list. */
+      t = timeout_heap_remove(ev->timeouts);
+      if(t->active) {
+       *tt = t;
+       tt = &t->next;
+      }
+    }
+    *tt = 0;
+    /* Now we can run the callbacks for those timeouts.  They might add further
+     * timeouts that are already in the past but they won't trigger until the
+     * next time round the event loop. */
+    for(t = timeouts; t; t = t->next) {
       D(("calling timeout for %ld.%ld callback %p %p",
         (long)t->when.tv_sec, (long)t->when.tv_usec,
         (void *)t->callback, t->u));
       D(("calling timeout for %ld.%ld callback %p %p",
         (long)t->when.tv_sec, (long)t->when.tv_usec,
         (void *)t->callback, t->u));
@@ -213,13 +209,6 @@ int ev_run(ev_source *ev) {
       if(ret)
        return ret;
     }
       if(ret)
        return ret;
     }
-    tt = &ev->timeouts;
-    while((t = *tt)) {
-      if(t->resolve)
-       *tt = t->next;
-      else
-       tt = &t->next;
-    }
     maxfd = 0;
     for(mode = 0; mode < ev_nmodes; ++mode) {
       ev->mode[mode].tripped = ev->mode[mode].enabled;
     maxfd = 0;
     for(mode = 0; mode < ev_nmodes; ++mode) {
       ev->mode[mode].tripped = ev->mode[mode].enabled;
@@ -228,10 +217,11 @@ int ev_run(ev_source *ev) {
     }
     xsigprocmask(SIG_UNBLOCK, &ev->sigmask, 0);
     do {
     }
     xsigprocmask(SIG_UNBLOCK, &ev->sigmask, 0);
     do {
-      if(ev->timeouts) {
+      if(timeout_heap_count(ev->timeouts)) {
+       t = timeout_heap_first(ev->timeouts);
        xgettimeofday(&now, 0);
        xgettimeofday(&now, 0);
-       delta.tv_sec = ev->timeouts->when.tv_sec - now.tv_sec;
-       delta.tv_usec = ev->timeouts->when.tv_usec - now.tv_usec;
+       delta.tv_sec = t->when.tv_sec - now.tv_sec;
+       delta.tv_usec = t->when.tv_usec - now.tv_usec;
        if(delta.tv_usec < 0) {
          delta.tv_usec += 1000000;
          --delta.tv_sec;
        if(delta.tv_usec < 0) {
          delta.tv_usec += 1000000;
          --delta.tv_sec;
@@ -321,6 +311,8 @@ int ev_fd(ev_source *ev,
 
   D(("registering %s fd %d callback %p %p", modenames[mode], fd,
      (void *)callback, u));
 
   D(("registering %s fd %d callback %p %p", modenames[mode], fd,
      (void *)callback, u));
+  if(fd >= FD_SETSIZE)
+    return -1;
   assert(mode < ev_nmodes);
   if(ev->mode[mode].nfds >= ev->mode[mode].fdslots) {
     ev->mode[mode].fdslots = (ev->mode[mode].fdslots
   assert(mode < ev_nmodes);
   if(ev->mode[mode].nfds >= ev->mode[mode].fdslots) {
     ev->mode[mode].fdslots = (ev->mode[mode].fdslots
@@ -473,7 +465,7 @@ int ev_timeout(ev_source *ev,
               const struct timeval *when,
               ev_timeout_callback *callback,
               void *u) {
               const struct timeval *when,
               ev_timeout_callback *callback,
               void *u) {
-  struct timeout *t, *p, **pp;
+  struct timeout *t;
 
   D(("registering timeout at %ld.%ld callback %p %p",
      when ? (long)when->tv_sec : 0, when ? (long)when->tv_usec : 0,
 
   D(("registering timeout at %ld.%ld callback %p %p",
      when ? (long)when->tv_sec : 0, when ? (long)when->tv_usec : 0,
@@ -483,11 +475,8 @@ int ev_timeout(ev_source *ev,
     t->when = *when;
   t->callback = callback;
   t->u = u;
     t->when = *when;
   t->callback = callback;
   t->u = u;
-  pp = &ev->timeouts;
-  while((p = *pp) && gt(&t->when, &p->when))
-    pp = &p->next;
-  t->next = p;
-  *pp = t;
+  t->active = 1;
+  timeout_heap_insert(ev->timeouts, t);
   if(handlep)
     *handlep = t;
   return 0;
   if(handlep)
     *handlep = t;
   return 0;
@@ -500,19 +489,13 @@ int ev_timeout(ev_source *ev,
  *
  * If @p handle is 0 then this is a no-op.
  */
  *
  * If @p handle is 0 then this is a no-op.
  */
-int ev_timeout_cancel(ev_source *ev,
+int ev_timeout_cancel(ev_source attribute((unused)) *ev,
                      ev_timeout_handle handle) {
                      ev_timeout_handle handle) {
-  struct timeout *t = handle, *p, **pp;
+  struct timeout *t = handle;
 
 
-  if(!t)
-    return 0;
-  for(pp = &ev->timeouts; (p = *pp) && p != t; pp = &p->next)
-    ;
-  if(p) {
-    *pp = p->next;
-    return 0;
-  } else
-    return -1;
+  if(t)
+    t->active = 0;
+  return 0;
 }
 
 /* signals ********************************************************************/
 }
 
 /* signals ********************************************************************/
@@ -536,7 +519,9 @@ static void sighandler(int s) {
 
   /* probably the reader has stopped listening for some reason */
   if(write(sigfd[s], &sc, 1) < 0) {
 
   /* probably the reader has stopped listening for some reason */
   if(write(sigfd[s], &sc, 1) < 0) {
-    write(2, errmsg, sizeof errmsg - 1);
+       /* do the best we can as we're about to abort; shut _up_, gcc */
+       int _ignore = write(2, errmsg, sizeof errmsg - 1);
+       (void)_ignore;
     abort();
   }
 }
     abort();
   }
 }
@@ -930,6 +915,9 @@ struct ev_writer {
 
   /** @brief Tied reader or 0 */
   ev_reader *reader;
 
   /** @brief Tied reader or 0 */
   ev_reader *reader;
+
+  /** @brief Set when abandoned */
+  int abandoned;
 };
 
 /** @brief State structure for a buffered reader */
 };
 
 /** @brief State structure for a buffered reader */
@@ -998,9 +986,12 @@ static int writer_timebound_exceeded(ev_source *ev,
                                     void *u) {
   ev_writer *const w = u;
 
                                     void *u) {
   ev_writer *const w = u;
 
-  error(0, "abandoning writer %s because no writes within %ds",
-       w->what, w->timebound);
-  w->error = ETIMEDOUT;
+  if(!w->abandoned) {
+    w->abandoned = 1;
+    error(0, "abandoning writer '%s' because no writes within %ds",
+         w->what, w->timebound);
+    w->error = ETIMEDOUT;
+  }
   return writer_shutdown(ev, now, u);
 }
 
   return writer_shutdown(ev, now, u);
 }
 
@@ -1075,11 +1066,15 @@ static int ev_writer_write(struct sink *sk, const void *s, int n) {
     /* The new buffer contents will exceed the space bound.  We assume that the
      * remote client has gone away and TCP hasn't noticed yet, or that it's got
      * hopelessly stuck. */
     /* The new buffer contents will exceed the space bound.  We assume that the
      * remote client has gone away and TCP hasn't noticed yet, or that it's got
      * hopelessly stuck. */
-    error(0, "abandoning writer %s because buffer has reached %td bytes",
-         w->what, w->b.end - w->b.start);
-    ev_fd_disable(w->ev, ev_write, w->fd);
-    w->error = EPIPE;
-    return ev_timeout(w->ev, 0, 0, writer_shutdown, w);
+    if(!w->abandoned) {
+      w->abandoned = 1;
+      error(0, "abandoning writer '%s' because buffer has reached %td bytes",
+           w->what, w->b.end - w->b.start);
+      ev_fd_disable(w->ev, ev_write, w->fd);
+      w->error = EPIPE;
+      return ev_timeout(w->ev, 0, 0, writer_shutdown, w);
+    } else
+      return 0;
   }
   /* Make sure there is space */
   buffer_space(&w->b, n);
   }
   /* Make sure there is space */
   buffer_space(&w->b, n);
@@ -1224,7 +1219,7 @@ int ev_writer_flush(ev_writer *w) {
 
 /* buffered reader ************************************************************/
 
 
 /* buffered reader ************************************************************/
 
-/** @brief Shut down a reader*
+/** @brief Shut down a reader
  *
  * This is the only path through which we cancel and close the file descriptor.
  * As with the writer case it is given timeout signature to allow it be
  *
  * This is the only path through which we cancel and close the file descriptor.
  * As with the writer case it is given timeout signature to allow it be