summaryrefslogtreecommitdiff
path: root/lib/libevent/event.c
diff options
context:
space:
mode:
authorBrad Smith <brad@cvs.openbsd.org>2005-04-22 00:56:26 +0000
committerBrad Smith <brad@cvs.openbsd.org>2005-04-22 00:56:26 +0000
commit9657663ceec9fe6a3be90c6e4bf3ea973058e65a (patch)
tree671952b73b614f5090d12c04adbb65afd196c12f /lib/libevent/event.c
parent6ca0465503b5d87bd9dca1cbaadf8de986104de6 (diff)
update to libevent 1.0c; keep local changes
no shared lib so no ABI/API check is necessary thanks to Alexander von Gernler for submitting another diff in an attempt to update libevent and for a use-after-free fix. ok henning@ deraadt@
Diffstat (limited to 'lib/libevent/event.c')
-rw-r--r--lib/libevent/event.c408
1 files changed, 294 insertions, 114 deletions
diff --git a/lib/libevent/event.c b/lib/libevent/event.c
index 42001fd34fc..0711f7d8a16 100644
--- a/lib/libevent/event.c
+++ b/lib/libevent/event.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: event.c,v 1.8 2005/04/19 08:07:45 deraadt Exp $ */
+/* $OpenBSD: event.c,v 1.9 2005/04/22 00:56:25 brad Exp $ */
/*
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
@@ -52,17 +52,11 @@
#include <errno.h>
#include <signal.h>
#include <string.h>
-#include <err.h>
#include <assert.h>
-#ifdef USE_LOG
-#include "log.h"
-#else
-#define LOG_DBG(x)
-#define log_error(x) perror(x)
-#endif
-
#include "event.h"
+#include "event-internal.h"
+#include "log.h"
#ifdef HAVE_SELECT
extern const struct eventop selectops;
@@ -79,6 +73,9 @@ extern const struct eventop epollops;
#ifdef HAVE_WORKING_KQUEUE
extern const struct eventop kqops;
#endif
+#ifdef HAVE_DEVPOLL
+extern const struct eventop devpollops;
+#endif
#ifdef WIN32
extern const struct eventop win32ops;
#endif
@@ -91,6 +88,9 @@ const struct eventop *eventops[] = {
#ifdef HAVE_EPOLL
&epollops,
#endif
+#ifdef HAVE_DEVPOLL
+ &devpollops,
+#endif
#ifdef HAVE_RTSIG
&rtsigops,
#endif
@@ -106,26 +106,25 @@ const struct eventop *eventops[] = {
NULL
};
-const struct eventop *evsel;
-void *evbase;
+/* Global state */
+struct event_list signalqueue;
+
+struct event_base *current_base = NULL;
/* Handle signals - This is a deprecated interface */
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
volatile sig_atomic_t event_gotsig; /* Set in signal handler */
-volatile sig_atomic_t event_gotterm; /* Set to terminate loop */
/* Prototypes */
-void event_queue_insert(struct event *, int);
-void event_queue_remove(struct event *, int);
-int event_haveevents(void);
+static void event_queue_insert(struct event_base *, struct event *, int);
+static void event_queue_remove(struct event_base *, struct event *, int);
+static int event_haveevents(struct event_base *);
-static void event_process_active(void);
+static void event_process_active(struct event_base *);
-static RB_HEAD(event_tree, event) timetree;
-static struct event_list activequeue;
-struct event_list signalqueue;
-struct event_list eventqueue;
-static struct timeval event_tv;
+static int timeout_next(struct event_base *, struct timeval *);
+static void timeout_process(struct event_base *);
+static void timeout_correct(struct event_base *, struct timeval *);
static int
compare(struct event *a, struct event *b)
@@ -136,7 +135,7 @@ compare(struct event *a, struct event *b)
return (1);
if (a < b)
return (-1);
- if (a > b)
+ else if (a > b)
return (1);
return (0);
}
@@ -146,55 +145,112 @@ RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
RB_GENERATE(event_tree, event, ev_timeout_node, compare);
-void
+void *
event_init(void)
{
int i;
+ if ((current_base = calloc(1, sizeof(struct event_base))) == NULL)
+ event_err(1, "%s: calloc");
+
event_sigcb = NULL;
event_gotsig = 0;
- gettimeofday(&event_tv, NULL);
+ gettimeofday(&current_base->event_tv, NULL);
- RB_INIT(&timetree);
- TAILQ_INIT(&eventqueue);
- TAILQ_INIT(&activequeue);
+ RB_INIT(&current_base->timetree);
+ TAILQ_INIT(&current_base->eventqueue);
TAILQ_INIT(&signalqueue);
- evbase = NULL;
- for (i = 0; eventops[i] && !evbase; i++) {
- evsel = eventops[i];
+ current_base->evbase = NULL;
+ for (i = 0; eventops[i] && !current_base->evbase; i++) {
+ current_base->evsel = eventops[i];
- evbase = evsel->init();
+ current_base->evbase = current_base->evsel->init();
}
- if (evbase == NULL)
- errx(1, "%s: no event mechanism available", __func__);
+ if (current_base->evbase == NULL)
+ event_errx(1, "%s: no event mechanism available", __func__);
if (!issetugid() && getenv("EVENT_SHOW_METHOD"))
- fprintf(stderr, "libevent using: %s\n", evsel->name);
+ event_msgx("libevent using: %s\n",
+ current_base->evsel->name);
-#if defined(USE_LOG) && defined(USE_DEBUG)
- log_to(stderr);
- log_debug_cmd(LOG_MISC, 80);
-#endif
+ /* allocate a single active event queue */
+ event_base_priority_init(current_base, 1);
+
+ return (current_base);
+}
+
+int
+event_priority_init(int npriorities)
+{
+ return event_base_priority_init(current_base, npriorities);
+}
+
+int
+event_base_priority_init(struct event_base *base, int npriorities)
+{
+ int i;
+
+ if (base->event_count_active)
+ return (-1);
+
+ if (base->nactivequeues && npriorities != base->nactivequeues) {
+ for (i = 0; i < base->nactivequeues; ++i) {
+ free(base->activequeues[i]);
+ }
+ free(base->activequeues);
+ }
+
+ /* Allocate our priority queues */
+ base->nactivequeues = npriorities;
+ base->activequeues = (struct event_list **)calloc(base->nactivequeues,
+ npriorities * sizeof(struct event_list *));
+ if (base->activequeues == NULL)
+ event_err(1, "%s: calloc", __func__);
+
+ for (i = 0; i < base->nactivequeues; ++i) {
+ base->activequeues[i] = malloc(sizeof(struct event_list));
+ if (base->activequeues[i] == NULL)
+ event_err(1, "%s: malloc", __func__);
+ TAILQ_INIT(base->activequeues[i]);
+ }
+
+ return (0);
}
int
-event_haveevents(void)
+event_haveevents(struct event_base *base)
{
- return (RB_ROOT(&timetree) || TAILQ_FIRST(&eventqueue) ||
- TAILQ_FIRST(&signalqueue) || TAILQ_FIRST(&activequeue));
+ return (base->event_count > 0);
}
+/*
+ * Active events are stored in priority queues. Lower priorities are always
+ * process before higher priorities. Low priority events can starve high
+ * priority ones.
+ */
+
static void
-event_process_active(void)
+event_process_active(struct event_base *base)
{
struct event *ev;
+ struct event_list *activeq = NULL;
+ int i;
short ncalls;
- for (ev = TAILQ_FIRST(&activequeue); ev;
- ev = TAILQ_FIRST(&activequeue)) {
- event_queue_remove(ev, EVLIST_ACTIVE);
+ if (!base->event_count_active)
+ return;
+
+ for (i = 0; i < base->nactivequeues; ++i) {
+ if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
+ activeq = base->activequeues[i];
+ break;
+ }
+ }
+
+ for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
@@ -217,36 +273,64 @@ event_dispatch(void)
return (event_loop(0));
}
+int
+event_base_dispatch(struct event_base *event_base)
+{
+ return (event_base_loop(event_base, 0));
+}
+
static void
event_loopexit_cb(int fd, short what, void *arg)
{
- event_gotterm = 1;
+ struct event_base *base = arg;
+ base->event_gotterm = 1;
}
+/* not thread safe */
+
int
event_loopexit(struct timeval *tv)
{
- return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, NULL, tv));
+ return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
+ current_base, tv));
}
int
+event_base_loopexit(struct event_base *event_base, struct timeval *tv)
+{
+ return (event_once(-1, EV_TIMEOUT, event_loopexit_cb,
+ event_base, tv));
+}
+
+/* not thread safe */
+
+int
event_loop(int flags)
{
+ return event_base_loop(current_base, flags);
+}
+
+int
+event_base_loop(struct event_base *base, int flags)
+{
+ const struct eventop *evsel = base->evsel;
+ void *evbase = base->evbase;
struct timeval tv;
int res, done;
/* Calculate the initial events that we are waiting for */
- if (evsel->recalc(evbase, 0) == -1)
+ if (evsel->recalc(base, evbase, 0) == -1)
return (-1);
done = 0;
while (!done) {
/* Terminate the loop if we have been asked to */
- if (event_gotterm) {
- event_gotterm = 0;
+ if (base->event_gotterm) {
+ base->event_gotterm = 0;
break;
}
+ /* You cannot use this interface for multi-threaded apps */
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
@@ -260,44 +344,45 @@ event_loop(int flags)
/* Check if time is running backwards */
gettimeofday(&tv, NULL);
- if (timercmp(&tv, &event_tv, <)) {
+ if (timercmp(&tv, &base->event_tv, <)) {
struct timeval off;
- LOG_DBG((LOG_MISC, 10,
- "%s: time is running backwards, corrected",
+ event_debug(("%s: time is running backwards, corrected",
__func__));
-
- timersub(&event_tv, &tv, &off);
- timeout_correct(&off);
+ timersub(&base->event_tv, &tv, &off);
+ timeout_correct(base, &off);
}
- event_tv = tv;
+ base->event_tv = tv;
- if (!(flags & EVLOOP_NONBLOCK))
- timeout_next(&tv);
+ if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK))
+ timeout_next(base, &tv);
else
timerclear(&tv);
/* If we have no events, we just exit */
- if (!event_haveevents())
+ if (!event_haveevents(base)) {
+ event_debug(("%s: no events registered.", __func__));
return (1);
+ }
- res = evsel->dispatch(evbase, &tv);
+ res = evsel->dispatch(base, evbase, &tv);
if (res == -1)
return (-1);
- timeout_process();
+ timeout_process(base);
- if (TAILQ_FIRST(&activequeue)) {
- event_process_active();
- if (flags & EVLOOP_ONCE)
+ if (base->event_count_active) {
+ event_process_active(base);
+ if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
- if (evsel->recalc(evbase, 0) == -1)
+ if (evsel->recalc(base, evbase, 0) == -1)
return (-1);
}
+ event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
@@ -337,15 +422,15 @@ event_once(int fd, short events,
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
return (-1);
+ eonce->cb = callback;
+ eonce->arg = arg;
+
if (events == EV_TIMEOUT) {
if (tv == NULL) {
timerclear(&etv);
tv = &etv;
}
- eonce->cb = callback;
- eonce->arg = arg;
-
evtimer_set(&eonce->ev, event_once_cb, eonce);
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
@@ -365,18 +450,50 @@ void
event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
+ /* Take the current base - caller needs to set the real base later */
+ ev->ev_base = current_base;
+
ev->ev_callback = callback;
ev->ev_arg = arg;
-#ifdef WIN32
- ev->ev_fd = (HANDLE)fd;
- ev->overlap.hEvent = ev;
-#else
ev->ev_fd = fd;
-#endif
ev->ev_events = events;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
+
+ /* by default, we put new events into the middle priority */
+ ev->ev_pri = current_base->nactivequeues/2;
+}
+
+int
+event_base_set(struct event_base *base, struct event *ev)
+{
+ /* Only innocent events may be assigned to a different base */
+ if (ev->ev_flags != EVLIST_INIT)
+ return (-1);
+
+ ev->ev_base = base;
+ ev->ev_pri = current_base->nactivequeues/2;
+
+ return (0);
+}
+
+/*
+ * Set's the priority of an event - if an event is already scheduled
+ * changing the priority is going to fail.
+ */
+
+int
+event_priority_set(struct event *ev, int pri)
+{
+ if (ev->ev_flags & EVLIST_ACTIVE)
+ return (-1);
+ if (pri < 0 || pri >= ev->ev_base->nactivequeues)
+ return (-1);
+
+ ev->ev_pri = pri;
+
+ return (0);
}
/*
@@ -409,7 +526,11 @@ event_pending(struct event *ev, short event, struct timeval *tv)
int
event_add(struct event *ev, struct timeval *tv)
{
- LOG_DBG((LOG_MISC, 55,
+ struct event_base *base = ev->ev_base;
+ const struct eventop *evsel = base->evsel;
+ void *evbase = base->evbase;
+
+ event_debug((
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
@@ -423,7 +544,7 @@ event_add(struct event *ev, struct timeval *tv)
struct timeval now;
if (ev->ev_flags & EVLIST_TIMEOUT)
- event_queue_remove(ev, EVLIST_TIMEOUT);
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
@@ -438,27 +559,27 @@ event_add(struct event *ev, struct timeval *tv)
*ev->ev_pncalls = 0;
}
- event_queue_remove(ev, EVLIST_ACTIVE);
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
}
gettimeofday(&now, NULL);
timeradd(&now, tv, &ev->ev_timeout);
- LOG_DBG((LOG_MISC, 55,
+ event_debug((
"event_add: timeout in %d seconds, call %p",
tv->tv_sec, ev->ev_callback));
- event_queue_insert(ev, EVLIST_TIMEOUT);
+ event_queue_insert(base, ev, EVLIST_TIMEOUT);
}
if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
- event_queue_insert(ev, EVLIST_INSERTED);
+ event_queue_insert(base, ev, EVLIST_INSERTED);
return (evsel->add(evbase, ev));
} else if ((ev->ev_events & EV_SIGNAL) &&
!(ev->ev_flags & EVLIST_SIGNAL)) {
- event_queue_insert(ev, EVLIST_SIGNAL);
+ event_queue_insert(base, ev, EVLIST_SIGNAL);
return (evsel->add(evbase, ev));
}
@@ -469,9 +590,21 @@ event_add(struct event *ev, struct timeval *tv)
int
event_del(struct event *ev)
{
- LOG_DBG((LOG_MISC, 80, "event_del: %p, callback %p",
+ struct event_base *base;
+ const struct eventop *evsel;
+ void *evbase;
+
+ event_debug(("event_del: %p, callback %p",
ev, ev->ev_callback));
+ /* An event without a base has not been added */
+ if (ev->ev_base == NULL)
+ return (-1);
+
+ base = ev->ev_base;
+ evsel = base->evsel;
+ evbase = base->evbase;
+
assert(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
@@ -481,16 +614,16 @@ event_del(struct event *ev)
}
if (ev->ev_flags & EVLIST_TIMEOUT)
- event_queue_remove(ev, EVLIST_TIMEOUT);
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
if (ev->ev_flags & EVLIST_ACTIVE)
- event_queue_remove(ev, EVLIST_ACTIVE);
+ event_queue_remove(base, ev, EVLIST_ACTIVE);
if (ev->ev_flags & EVLIST_INSERTED) {
- event_queue_remove(ev, EVLIST_INSERTED);
+ event_queue_remove(base, ev, EVLIST_INSERTED);
return (evsel->del(evbase, ev));
} else if (ev->ev_flags & EVLIST_SIGNAL) {
- event_queue_remove(ev, EVLIST_SIGNAL);
+ event_queue_remove(base, ev, EVLIST_SIGNAL);
return (evsel->del(evbase, ev));
}
@@ -509,18 +642,18 @@ event_active(struct event *ev, int res, short ncalls)
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
- event_queue_insert(ev, EVLIST_ACTIVE);
+ event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}
int
-timeout_next(struct timeval *tv)
+timeout_next(struct event_base *base, struct timeval *tv)
{
struct timeval dflt = TIMEOUT_DEFAULT;
struct timeval now;
struct event *ev;
- if ((ev = RB_MIN(event_tree, &timetree)) == NULL) {
+ if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
*tv = dflt;
return (0);
}
@@ -538,96 +671,143 @@ timeout_next(struct timeval *tv)
assert(tv->tv_sec >= 0);
assert(tv->tv_usec >= 0);
- LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
+ event_debug(("timeout_next: in %d seconds", tv->tv_sec));
return (0);
}
-void
-timeout_correct(struct timeval *off)
+static void
+timeout_correct(struct event_base *base, struct timeval *off)
{
struct event *ev;
- /* We can modify the key element of the node without destroying
+ /*
+ * We can modify the key element of the node without destroying
* the key, beause we apply it to all in the right order.
*/
- RB_FOREACH(ev, event_tree, &timetree)
+ RB_FOREACH(ev, event_tree, &base->timetree)
timersub(&ev->ev_timeout, off, &ev->ev_timeout);
}
void
-timeout_process(void)
+timeout_process(struct event_base *base)
{
struct timeval now;
struct event *ev, *next;
gettimeofday(&now, NULL);
- for (ev = RB_MIN(event_tree, &timetree); ev; ev = next) {
+ for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
if (timercmp(&ev->ev_timeout, &now, >))
break;
- next = RB_NEXT(event_tree, &timetree, ev);
+ next = RB_NEXT(event_tree, &base->timetree, ev);
- event_queue_remove(ev, EVLIST_TIMEOUT);
+ event_queue_remove(base, ev, EVLIST_TIMEOUT);
/* delete this event from the I/O queues */
event_del(ev);
- LOG_DBG((LOG_MISC, 60, "timeout_process: call %p",
+ event_debug(("timeout_process: call %p",
ev->ev_callback));
event_active(ev, EV_TIMEOUT, 1);
}
}
void
-event_queue_remove(struct event *ev, int queue)
+event_queue_remove(struct event_base *base, struct event *ev, int queue)
{
+ int docount = 1;
+
if (!(ev->ev_flags & queue))
- errx(1, "%s: %p(fd %d) not on queue %x", __func__,
- ev, ev->ev_fd, queue);
+ event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
+ ev, ev->ev_fd, queue);
+
+ if (ev->ev_flags & EVLIST_INTERNAL)
+ docount = 0;
+
+ if (docount)
+ base->event_count--;
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_ACTIVE:
- TAILQ_REMOVE(&activequeue, ev, ev_active_next);
+ if (docount)
+ base->event_count_active--;
+ TAILQ_REMOVE(base->activequeues[ev->ev_pri],
+ ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT:
- RB_REMOVE(event_tree, &timetree, ev);
+ RB_REMOVE(event_tree, &base->timetree, ev);
break;
case EVLIST_INSERTED:
- TAILQ_REMOVE(&eventqueue, ev, ev_next);
+ TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
break;
default:
- errx(1, "%s: unknown queue %x", __func__, queue);
+ event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
void
-event_queue_insert(struct event *ev, int queue)
+event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
- if (ev->ev_flags & queue)
- errx(1, "%s: %p(fd %d) already on queue %x", __func__,
- ev, ev->ev_fd, queue);
+ int docount = 1;
+
+ if (ev->ev_flags & queue) {
+ /* Double insertion is possible for active events */
+ if (queue & EVLIST_ACTIVE)
+ return;
+
+ event_errx(1, "%s: %p(fd %d) already on queue %x", __func__,
+ ev, ev->ev_fd, queue);
+ }
+
+ if (ev->ev_flags & EVLIST_INTERNAL)
+ docount = 0;
+
+ if (docount)
+ base->event_count++;
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_ACTIVE:
- TAILQ_INSERT_TAIL(&activequeue, ev, ev_active_next);
+ if (docount)
+ base->event_count_active++;
+ TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
+ ev,ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT: {
- struct event *tmp = RB_INSERT(event_tree, &timetree, ev);
+ struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
assert(tmp == NULL);
break;
}
case EVLIST_INSERTED:
- TAILQ_INSERT_TAIL(&eventqueue, ev, ev_next);
+ TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
break;
default:
- errx(1, "%s: unknown queue %x", __func__, queue);
+ event_errx(1, "%s: unknown queue %x", __func__, queue);
}
}
+
+/* Functions for debugging */
+
+const char *
+event_get_version(void)
+{
+ return (LIBEVENT_VERSION);
+}
+
+/*
+ * No thread-safe interface needed - the information should be the same
+ * for all threads.
+ */
+
+const char *
+event_get_method(void)
+{
+ return (current_base->evsel->name);
+}