66 files changed, 15542 insertions, 1 deletions
diff --git a/src/libevent/event.c b/src/libevent/event.c new file mode 100755 index 0000000..d6514cb --- a/dev/null +++ b/src/libevent/event.c @@ -0,0 +1,645 @@ +/*********************************************************************** +* +* event.c +* +* Abstraction of select call into "event-handling" to make programming +* easier. +* +* Copyright (C) 2001 Roaring Penguin Software Inc. +* +* This program may be distributed according to the terms of the GNU +* General Public License, version 2 or (at your option) any later version. +* +* LIC: GPL +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "event.h" +#include <stdlib.h> +#include <errno.h> + +static void DestroySelector(EventSelector *es); +static void DestroyHandler(EventHandler *eh); +static void DoPendingChanges(EventSelector *es); + +/********************************************************************** +* %FUNCTION: Event_CreateSelector +* %ARGUMENTS: +* None +* %RETURNS: +* A newly-allocated EventSelector, or NULL if out of memory. +* %DESCRIPTION: +* Creates a new EventSelector. +***********************************************************************/ +EventSelector * +Event_CreateSelector(void) +{ + EventSelector *es = malloc(sizeof(EventSelector)); + if (!es) return NULL; + es->handlers = NULL; + es->nestLevel = 0; + es->destroyPending = 0; + es->opsPending = 0; + EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es)); + return es; +} + +/********************************************************************** +* %FUNCTION: Event_DestroySelector +* %ARGUMENTS: +* es -- EventSelector to destroy +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys an EventSelector. Destruction may be delayed if we +* are in the HandleEvent function. +***********************************************************************/ +void +Event_DestroySelector(EventSelector *es) +{ + if (es->nestLevel) { + es->destroyPending = 1; + es->opsPending = 1; + return; + } + DestroySelector(es); +} + +/********************************************************************** +* %FUNCTION: Event_HandleEvent +* %ARGUMENTS: +* es -- EventSelector +* %RETURNS: +* 0 if OK, non-zero on error. errno is set appropriately. +* %DESCRIPTION: +* Handles a single event (uses select() to wait for an event.) +***********************************************************************/ +int +Event_HandleEvent(EventSelector *es) +{ + fd_set readfds, writefds; + fd_set *rd, *wr; + unsigned int flags; + + struct timeval abs_timeout, now; + + struct timeval timeout; + struct timeval *tm; + EventHandler *eh; + + int r = 0; + int errno_save = 0; + int foundTimeoutEvent = 0; + int foundReadEvent = 0; + int foundWriteEvent = 0; + int maxfd = -1; + int pastDue; + + /* Avoid compiler warning */ + abs_timeout.tv_sec = 0; + abs_timeout.tv_usec = 0; + + EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es)); + + /* Build the select sets */ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + eh = es->handlers; + for (eh=es->handlers; eh; eh=eh->next) { + if (eh->flags & EVENT_FLAG_DELETED) continue; + if (eh->flags & EVENT_FLAG_READABLE) { + foundReadEvent = 1; + FD_SET(eh->fd, &readfds); + if (eh->fd > maxfd) maxfd = eh->fd; + } + if (eh->flags & EVENT_FLAG_WRITEABLE) { + foundWriteEvent = 1; + FD_SET(eh->fd, &writefds); + if (eh->fd > maxfd) maxfd = eh->fd; + } + if (eh->flags & EVENT_TIMER_BITS) { + if (!foundTimeoutEvent) { + abs_timeout = eh->tmout; + foundTimeoutEvent = 1; + } else { + if (eh->tmout.tv_sec < abs_timeout.tv_sec || + (eh->tmout.tv_sec == abs_timeout.tv_sec && + eh->tmout.tv_usec < abs_timeout.tv_usec)) { + abs_timeout = eh->tmout; + } + } + } + } + if (foundReadEvent) { + rd = &readfds; + } else { + rd = NULL; + } + if (foundWriteEvent) { + wr = &writefds; + } else { + wr = NULL; + } + + if (foundTimeoutEvent) { + gettimeofday(&now, NULL); + /* Convert absolute timeout to relative timeout for select */ + timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec; + timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec; + if (timeout.tv_usec < 0) { + timeout.tv_usec += 1000000; + timeout.tv_sec--; + } + if (timeout.tv_sec < 0 || + (timeout.tv_sec == 0 && timeout.tv_usec < 0)) { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + tm = &timeout; + } else { + tm = NULL; + } + + if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) { + for(;;) { + r = select(maxfd+1, rd, wr, NULL, tm); + if (r < 0) { + if (errno == EINTR) continue; + } + break; + } + } + + if (foundTimeoutEvent) gettimeofday(&now, NULL); + errno_save = errno; + es->nestLevel++; + + if (r >= 0) { + /* Call handlers */ + for (eh=es->handlers; eh; eh=eh->next) { + + /* Pending delete for this handler? Ignore it */ + if (eh->flags & EVENT_FLAG_DELETED) continue; + + flags = 0; + if ((eh->flags & EVENT_FLAG_READABLE) && + FD_ISSET(eh->fd, &readfds)) { + flags |= EVENT_FLAG_READABLE; + } + if ((eh->flags & EVENT_FLAG_WRITEABLE) && + FD_ISSET(eh->fd, &writefds)) { + flags |= EVENT_FLAG_WRITEABLE; + } + if (eh->flags & EVENT_TIMER_BITS) { + pastDue = (eh->tmout.tv_sec < now.tv_sec || + (eh->tmout.tv_sec == now.tv_sec && + eh->tmout.tv_usec <= now.tv_usec)); + if (pastDue) { + flags |= EVENT_TIMER_BITS; + if (eh->flags & EVENT_FLAG_TIMER) { + /* Timer events are only called once */ + es->opsPending = 1; + eh->flags |= EVENT_FLAG_DELETED; + } + } + } + /* Do callback */ + if (flags) { + EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags)); + eh->fn(es, eh->fd, flags, eh->data); + EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags)); + } + } + } + + es->nestLevel--; + + if (!es->nestLevel && es->opsPending) { + DoPendingChanges(es); + } + errno = errno_save; + return r; +} + +/********************************************************************** +* %FUNCTION: Event_AddHandler +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor to watch +* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddHandler(EventSelector *es, + int fd, + unsigned int flags, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + + /* Specifically disable timer and deleted flags */ + flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED)); + + /* Bad file descriptor */ + if (fd < 0) { + errno = EBADF; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + eh->fd = fd; + eh->flags = flags; + eh->tmout.tv_usec = 0; + eh->tmout.tv_sec = 0; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh)); + return eh; +} + +/********************************************************************** +* %FUNCTION: Event_AddHandlerWithTimeout +* %ARGUMENTS: +* es -- event selector +* fd -- file descriptor to watch +* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE +* t -- Timeout after which to call handler, even if not readable/writable. +* If t.tv_sec < 0, calls normal Event_AddHandler with no timeout. +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddHandlerWithTimeout(EventSelector *es, + int fd, + unsigned int flags, + struct timeval t, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + struct timeval now; + + /* If timeout is negative, just do normal non-timing-out event */ + if (t.tv_sec < 0 || t.tv_usec < 0) { + return Event_AddHandler(es, fd, flags, fn, data); + } + + /* Specifically disable timer and deleted flags */ + flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED)); + flags |= EVENT_FLAG_TIMEOUT; + + /* Bad file descriptor? */ + if (fd < 0) { + errno = EBADF; + return NULL; + } + + /* Bad timeout? */ + if (t.tv_usec >= 1000000) { + errno = EINVAL; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + eh->fd = fd; + eh->flags = flags; + eh->tmout = t; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh)); + return eh; +} + + +/********************************************************************** +* %FUNCTION: Event_AddTimerHandler +* %ARGUMENTS: +* es -- event selector +* t -- time interval after which to trigger event +* fn -- callback function to call when event is triggered +* data -- extra data to pass to callback function +* %RETURNS: +* A newly-allocated EventHandler, or NULL. +***********************************************************************/ +EventHandler * +Event_AddTimerHandler(EventSelector *es, + struct timeval t, + EventCallbackFunc fn, + void *data) +{ + EventHandler *eh; + struct timeval now; + + /* Check time interval for validity */ + if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { + errno = EINVAL; + return NULL; + } + + eh = malloc(sizeof(EventHandler)); + if (!eh) return NULL; + + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + eh->fd = -1; + eh->flags = EVENT_FLAG_TIMER; + eh->tmout = t; + eh->fn = fn; + eh->data = data; + + /* Add immediately. This is safe even if we are in a handler. */ + eh->next = es->handlers; + es->handlers = eh; + + EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh)); + return eh; +} + +/********************************************************************** +* %FUNCTION: Event_DelHandler +* %ARGUMENTS: +* es -- event selector +* eh -- event handler +* %RETURNS: +* 0 if OK, non-zero if there is an error +* %DESCRIPTION: +* Deletes the event handler eh +***********************************************************************/ +int +Event_DelHandler(EventSelector *es, + EventHandler *eh) +{ + /* Scan the handlers list */ + EventHandler *cur, *prev; + EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh)); + for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) { + if (cur == eh) { + if (es->nestLevel) { + eh->flags |= EVENT_FLAG_DELETED; + es->opsPending = 1; + return 0; + } else { + if (prev) prev->next = cur->next; + else es->handlers = cur->next; + + DestroyHandler(cur); + return 0; + } + } + } + + /* Handler not found */ + return 1; +} + +/********************************************************************** +* %FUNCTION: DestroySelector +* %ARGUMENTS: +* es -- an event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys selector and all associated handles. +***********************************************************************/ +void +DestroySelector(EventSelector *es) +{ + EventHandler *cur, *next; + for (cur=es->handlers; cur; cur=next) { + next = cur->next; + DestroyHandler(cur); + } + + free(es); +} + +/********************************************************************** +* %FUNCTION: DestroyHandler +* %ARGUMENTS: +* eh -- an event handler +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Destroys handler +***********************************************************************/ +void +DestroyHandler(EventHandler *eh) +{ + EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh)); + free(eh); +} + +/********************************************************************** +* %FUNCTION: DoPendingChanges +* %ARGUMENTS: +* es -- an event selector +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Makes all pending insertions and deletions happen. +***********************************************************************/ +void +DoPendingChanges(EventSelector *es) +{ + EventHandler *cur, *prev, *next; + + es->opsPending = 0; + + /* If selector is to be deleted, do it and skip everything else */ + if (es->destroyPending) { + DestroySelector(es); + return; + } + + /* Do deletions */ + cur = es->handlers; + prev = NULL; + while(cur) { + if (!(cur->flags & EVENT_FLAG_DELETED)) { + prev = cur; + cur = cur->next; + continue; + } + + /* Unlink from list */ + if (prev) { + prev->next = cur->next; + } else { + es->handlers = cur->next; + } + next = cur->next; + DestroyHandler(cur); + cur = next; + } +} + +/********************************************************************** +* %FUNCTION: Event_GetCallback +* %ARGUMENTS: +* eh -- the event handler +* %RETURNS: +* The callback function +***********************************************************************/ +EventCallbackFunc +Event_GetCallback(EventHandler *eh) +{ + return eh->fn; +} + +/********************************************************************** +* %FUNCTION: Event_GetData +* %ARGUMENTS: +* eh -- the event handler +* %RETURNS: +* The "data" field. +***********************************************************************/ +void * +Event_GetData(EventHandler *eh) +{ + return eh->data; +} + +/********************************************************************** +* %FUNCTION: Event_SetCallbackAndData +* %ARGUMENTS: +* eh -- the event handler +* fn -- new callback function +* data -- new data value +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Sets the callback function and data fields. +***********************************************************************/ +void +Event_SetCallbackAndData(EventHandler *eh, + EventCallbackFunc fn, + void *data) +{ + eh->fn = fn; + eh->data = data; +} + +#ifdef DEBUG_EVENT +#include <stdarg.h> +#include <stdio.h> +FILE *Event_DebugFP = NULL; +/********************************************************************** +* %FUNCTION: Event_DebugMsg +* %ARGUMENTS: +* fmt, ... -- format string +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Writes a debug message to the debug file. +***********************************************************************/ +void +Event_DebugMsg(char const *fmt, ...) +{ + va_list ap; + struct timeval now; + + if (!Event_DebugFP) return; + + gettimeofday(&now, NULL); + + fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000, + (int) now.tv_usec / 1000); + + va_start(ap, fmt); + vfprintf(Event_DebugFP, fmt, ap); + va_end(ap); + fflush(Event_DebugFP); +} + +#endif + +/********************************************************************** +* %FUNCTION: Event_EnableDebugging +* %ARGUMENTS: +* fname -- name of file to log debug messages to +* %RETURNS: +* 1 if debugging was enabled; 0 otherwise. +***********************************************************************/ +int +Event_EnableDebugging(char const *fname) +{ +#ifndef DEBUG_EVENT + return 0; +#else + Event_DebugFP = fopen(fname, "w"); + return (Event_DebugFP != NULL); +#endif +} + +/********************************************************************** +* %FUNCTION: Event_ChangeTimeout +* %ARGUMENTS: +* h -- event handler +* t -- new timeout +* %RETURNS: +* Nothing +* %DESCRIPTION: +* Changes timeout of event handler to be "t" seconds in the future. +***********************************************************************/ +void +Event_ChangeTimeout(EventHandler *h, struct timeval t) +{ + struct timeval now; + + /* Check time interval for validity */ + if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { + return; + } + /* Convert time interval to absolute time */ + gettimeofday(&now, NULL); + + t.tv_sec += now.tv_sec; + t.tv_usec += now.tv_usec; + if (t.tv_usec >= 1000000) { + t.tv_usec -= 1000000; + t.tv_sec++; + } + + h->tmout = t; +} |