X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/1563f6f65b853924d53c90f736f6c2486f613cb0..985bb670b4e07d35cb1580780253ded2524a342e:/lib/event.c
diff --git a/lib/event.c b/lib/event.c
index c42eca3..ee25024 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -1,27 +1,25 @@
/*
* 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
- * 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.
- *
- * 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
- * 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 .
*/
/** @file lib/event.c
* @brief DisOrder event loop
*/
-#include
+#include "common.h"
#include
#include
@@ -31,15 +29,11 @@
#include
#include
#include
-#include
#include
#include
-#include
-#include
#include
#include
#include
-#include
#include "event.h"
#include "mem.h"
#include "log.h"
@@ -47,6 +41,8 @@
#include "printf.h"
#include "sink.h"
#include "vector.h"
+#include "timeval.h"
+#include "heap.h"
/** @brief A timeout */
struct timeout {
@@ -54,9 +50,18 @@ struct timeout {
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;
@@ -106,11 +111,8 @@ struct ev_source {
/** @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];
@@ -146,27 +148,6 @@ static const char *modenames[] = { "read", "write", "except" };
/* 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 @file 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 */
@@ -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);
+ timeout_heap_init(ev->timeouts);
return ev;
}
@@ -194,7 +176,7 @@ int ev_run(ev_source *ev) {
int n, mode;
int ret;
int maxfd;
- struct timeout *t, **tt;
+ struct timeout *timeouts, *t, **tt;
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. */
- 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));
@@ -213,13 +209,6 @@ int ev_run(ev_source *ev) {
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;
@@ -228,10 +217,11 @@ int ev_run(ev_source *ev) {
}
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);
- 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;
@@ -321,6 +311,8 @@ int ev_fd(ev_source *ev,
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
@@ -388,6 +380,7 @@ int ev_fd_cancel(ev_source *ev, ev_fdmode mode, int fd) {
* cancelled.
*/
int ev_fd_enable(ev_source *ev, ev_fdmode mode, int fd) {
+ assert(fd >= 0);
D(("enabling mode %s fd %d", modenames[mode], fd));
FD_SET(fd, &ev->mode[mode].enabled);
return 0;
@@ -418,15 +411,17 @@ void ev_report(ev_source *ev) {
struct dynstr d[1];
char b[4096];
+ if(!debugging)
+ return;
dynstr_init(d);
for(mode = 0; mode < ev_nmodes; ++mode) {
- info("mode %s maxfd %d", modenames[mode], ev->mode[mode].maxfd);
+ D(("mode %s maxfd %d", modenames[mode], ev->mode[mode].maxfd));
for(n = 0; n < ev->mode[mode].nfds; ++n) {
fd = ev->mode[mode].fds[n].fd;
- info("fd %s %d%s%s (%s)", modenames[mode], fd,
- FD_ISSET(fd, &ev->mode[mode].enabled) ? " enabled" : "",
- FD_ISSET(fd, &ev->mode[mode].tripped) ? " tripped" : "",
- ev->mode[mode].fds[n].what);
+ D(("fd %s %d%s%s (%s)", modenames[mode], fd,
+ FD_ISSET(fd, &ev->mode[mode].enabled) ? " enabled" : "",
+ FD_ISSET(fd, &ev->mode[mode].tripped) ? " tripped" : "",
+ ev->mode[mode].fds[n].what));
}
d->nvec = 0;
for(fd = 0; fd <= ev->mode[mode].maxfd; ++fd) {
@@ -444,7 +439,7 @@ void ev_report(ev_source *ev) {
dynstr_append_string(d, b);
}
dynstr_terminate(d);
- info("%s enabled:%s", modenames[mode], d->vec);
+ D(("%s enabled:%s", modenames[mode], d->vec));
}
}
@@ -452,7 +447,7 @@ void ev_report(ev_source *ev) {
/** @brief Register a timeout
* @param ev Event source
- * @param handle Where to store timeout handle, or @c NULL
+ * @param handlep Where to store timeout handle, or @c NULL
* @param when Earliest time to call @p callback, or @c NULL
* @param callback Function to call at or after @p when
* @param u Passed to @p callback
@@ -470,7 +465,7 @@ int ev_timeout(ev_source *ev,
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,
@@ -480,11 +475,8 @@ int ev_timeout(ev_source *ev,
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;
@@ -497,19 +489,13 @@ int ev_timeout(ev_source *ev,
*
* 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) {
- 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 ********************************************************************/
@@ -533,7 +519,9 @@ static void sighandler(int s) {
/* 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();
}
}
@@ -927,6 +915,9 @@ struct ev_writer {
/** @brief Tied reader or 0 */
ev_reader *reader;
+
+ /** @brief Set when abandoned */
+ int abandoned;
};
/** @brief State structure for a buffered reader */
@@ -971,17 +962,17 @@ static int writer_shutdown(ev_source *ev,
if(w->fd == -1)
return 0; /* already shut down */
- info("writer_shutdown fd=%d", w->fd);
+ D(("writer_shutdown fd=%d error=%d", w->fd, w->error));
ev_timeout_cancel(ev, w->timeout);
ev_fd_cancel(ev, ev_write, w->fd);
w->timeout = 0;
if(w->reader) {
- info("found a tied reader");
+ D(("found a tied reader"));
/* If there is a reader still around we just untie it */
w->reader->writer = 0;
shutdown(w->fd, SHUT_WR); /* there'll be no more writes */
} else {
- info("no tied reader");
+ D(("no tied reader"));
/* There's no reader so we are free to close the FD */
xclose(w->fd);
}
@@ -995,9 +986,12 @@ static int writer_timebound_exceeded(ev_source *ev,
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);
}
@@ -1066,15 +1060,21 @@ static int ev_writer_write(struct sink *sk, const void *s, int n) {
if(!n)
return 0; /* avoid silliness */
+ if(w->fd == -1)
+ error(0, "ev_writer_write on %s after shutdown", w->what);
if(w->spacebound && w->b.end - w->b.start + n > w->spacebound) {
/* 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);
@@ -1219,7 +1219,7 @@ int ev_writer_flush(ev_writer *w) {
/* 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
@@ -1235,16 +1235,16 @@ static int reader_shutdown(ev_source *ev,
if(r->fd == -1)
return 0; /* already shut down */
- info("reader_shutdown fd=%d", r->fd);
+ D(("reader_shutdown fd=%d", r->fd));
ev_fd_cancel(ev, ev_read, r->fd);
r->eof = 1;
if(r->writer) {
- info("found a tied writer");
+ D(("found a tied writer"));
/* If there is a writer still around we just untie it */
r->writer->reader = 0;
shutdown(r->fd, SHUT_RD); /* there'll be no more reads */
} else {
- info("no tied writer found");
+ D(("no tied writer found"));
/* There's no writer so we are free to close the FD */
xclose(r->fd);
}