From 131dc76d56df40b3511c47e54f15412a25b491f8 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 3 Feb 2005 11:56:03 +0000 Subject: r5197: moved events code to lib/events/ (suggestion from metze) (This used to be commit 7f54c8a339f36aa43c9340be70ab7f0067593ef2) --- source4/lib/basic.mk | 9 - source4/lib/events.c | 520 -------------------------------------- source4/lib/events/config.m4 | 2 + source4/lib/events/config.mk | 8 + source4/lib/events/events.c | 520 ++++++++++++++++++++++++++++++++++++++ source4/lib/events/events.h | 63 +++++ source4/lib/messaging/messaging.c | 2 +- 7 files changed, 594 insertions(+), 530 deletions(-) delete mode 100644 source4/lib/events.c create mode 100644 source4/lib/events/config.m4 create mode 100644 source4/lib/events/config.mk create mode 100644 source4/lib/events/events.c create mode 100644 source4/lib/events/events.h (limited to 'source4/lib') diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk index dda79f50ad..5f9a60e114 100644 --- a/source4/lib/basic.mk +++ b/source4/lib/basic.mk @@ -32,15 +32,6 @@ ADD_OBJ_FILES = \ # End SUBSYSTEM LIBCRYPTO ############################## -############################## -# Start SUBSYSTEM LIBEVENTS -[SUBSYSTEM::LIBEVENTS] -NOPROTO = YES -INIT_OBJ_FILES = lib/events.o -REQUIRED_SUBSYSTEMS = LIBTALLOC -# End SUBSYSTEM LIBEVENTS -############################## - ############################## # Start SUBSYSTEM LIBBASIC [SUBSYSTEM::LIBBASIC] diff --git a/source4/lib/events.c b/source4/lib/events.c deleted file mode 100644 index 4907a60f01..0000000000 --- a/source4/lib/events.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - Unix SMB/CIFS implementation. - main select loop and event handling - Copyright (C) Andrew Tridgell 2003 - - 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 - (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. - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - PLEASE READ THIS BEFORE MODIFYING! - - This module is a general abstraction for the main select loop and - event handling. Do not ever put any localised hacks in here, instead - register one of the possible event types and implement that event - somewhere else. - - There are 2 types of event handling that are handled in this module: - - 1) a file descriptor becoming readable or writeable. This is mostly - used for network sockets, but can be used for any type of file - descriptor. You may only register one handler for each file - descriptor/io combination or you will get unpredictable results - (this means that you can have a handler for read events, and a - separate handler for write events, but not two handlers that are - both handling read events) - - 2) a timed event. You can register an event that happens at a - specific time. You can register as many of these as you - like. They are single shot - add a new timed event in the event - handler to get another event. - - To setup a set of events you first need to create a event_context - structure using the function event_context_init(); This returns a - 'struct event_context' that you use in all subsequent calls. - - After that you can add/remove events that you are interested in - using event_add_*() and talloc_free() - - Finally, you call event_loop_wait() to block waiting for one of the - events to occor. In normal operation event_loop_wait() will loop - forever. - -*/ - -#include "includes.h" -#include "system/time.h" -#include "system/select.h" -#include "dlinklist.h" -#include "events.h" - -/* use epoll if it is available */ -#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_EPOLL_H) -#define WITH_EPOLL 1 -#endif - -#if WITH_EPOLL -#include -#endif - -struct event_context { - /* list of filedescriptor events */ - struct fd_event { - struct event_context *event_ctx; - struct fd_event *next, *prev; - int fd; - uint16_t flags; /* see EVENT_FD_* flags */ - event_fd_handler_t handler; - void *private; - } *fd_events; - - /* list of timed events */ - struct timed_event { - struct event_context *event_ctx; - struct timed_event *next, *prev; - struct timeval next_event; - event_timed_handler_t handler; - void *private; - } *timed_events; - - /* the maximum file descriptor number in fd_events */ - int maxfd; - - /* information for exiting from the event loop */ - int exit_code; - - /* this is changed by the destructors for the fd event - type. It is used to detect event destruction by event - handlers, which means the code that is calling the event - handler needs to assume that the linked list is no longer - valid - */ - uint32_t destruction_count; - -#if WITH_EPOLL - /* when using epoll this is the handle from epoll_create */ - int epoll_fd; -#endif -}; - - -/* - create a event_context structure. This must be the first events - call, and all subsequent calls pass this event_context as the first - element. Event handlers also receive this as their first argument. -*/ -struct event_context *event_context_init(TALLOC_CTX *mem_ctx) -{ - struct event_context *ev; - - ev = talloc_zero(mem_ctx, struct event_context); - if (!ev) return NULL; - -#if WITH_EPOLL - ev->epoll_fd = epoll_create(64); -#endif - - return ev; -} - - -/* - recalculate the maxfd -*/ -static void calc_maxfd(struct event_context *ev) -{ - struct fd_event *e; - ev->maxfd = 0; - for (e=ev->fd_events; e; e=e->next) { - if (e->fd > ev->maxfd) { - ev->maxfd = e->fd; - } - } -} - - -/* to mark the ev->maxfd invalid - * this means we need to recalculate it - */ -#define EVENT_INVALID_MAXFD (-1) - - -#if WITH_EPOLL -/* - called when a epoll call fails, and we should fallback - to using select -*/ -static void epoll_fallback_to_select(struct event_context *ev, const char *reason) -{ - DEBUG(0,("%s - using select() - %s\n", reason, strerror(errno))); - close(ev->epoll_fd); - ev->epoll_fd = -1; -} -#endif - - -#if WITH_EPOLL -/* - map from EVENT_FD_* to EPOLLIN/EPOLLOUT -*/ -static uint32_t epoll_map_flags(uint16_t flags) -{ - uint32_t ret = 0; - if (flags & EVENT_FD_READ) ret |= EPOLLIN; - if (flags & EVENT_FD_WRITE) ret |= EPOLLOUT; - return ret; -} -#endif - -/* - destroy an fd_event -*/ -static int event_fd_destructor(void *ptr) -{ - struct fd_event *fde = talloc_get_type(ptr, struct fd_event); - struct event_context *ev = fde->event_ctx; - - if (ev->maxfd == fde->fd) { - ev->maxfd = EVENT_INVALID_MAXFD; - } - DLIST_REMOVE(ev->fd_events, fde); - ev->destruction_count++; -#if WITH_EPOLL - if (ev->epoll_fd != -1) { - struct epoll_event event; - ZERO_STRUCT(event); - event.events = epoll_map_flags(fde->flags); - event.data.ptr = fde; - if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event) != 0) { - epoll_fallback_to_select(ev, "EPOLL_CTL_DEL failed"); - } - } -#endif - return 0; -} - -/* - add a fd based event - return NULL on failure (memory allocation error) -*/ -struct fd_event *event_add_fd(struct event_context *ev, TALLOC_CTX *mem_ctx, - int fd, uint16_t flags, event_fd_handler_t handler, - void *private) -{ - struct fd_event *e = talloc(ev, struct fd_event); - if (!e) return NULL; - - e->event_ctx = ev; - e->fd = fd; - e->flags = flags; - e->handler = handler; - e->private = private; - - DLIST_ADD(ev->fd_events, e); - - if (e->fd > ev->maxfd) { - ev->maxfd = e->fd; - } - - talloc_set_destructor(e, event_fd_destructor); - if (mem_ctx) { - talloc_steal(mem_ctx, e); - } - -#if WITH_EPOLL - if (ev->epoll_fd != -1) { - struct epoll_event event; - ZERO_STRUCT(event); - event.events = epoll_map_flags(flags); - event.data.ptr = e; - if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_ADD, e->fd, &event) != 0) { - epoll_fallback_to_select(ev, "EPOLL_CTL_ADD failed"); - } - } -#endif - - return e; -} - - -/* - return the fd event flags -*/ -uint16_t event_fd_flags(struct fd_event *fde) -{ - return fde?fde->flags:0; -} - -/* - set the fd event flags -*/ -void event_fd_setflags(struct fd_event *fde, uint16_t flags) -{ -#if WITH_EPOLL - struct event_context *ev; - if (fde == NULL) return; - ev = fde->event_ctx; - if (ev->epoll_fd != -1) { - struct epoll_event event; - ZERO_STRUCT(event); - event.events = epoll_map_flags(flags); - event.data.ptr = fde; - if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) { - epoll_fallback_to_select(ev, "EPOLL_CTL_MOD failed"); - } - } -#endif - if (fde) { - fde->flags = flags; - } -} - -/* - destroy a timed event -*/ -static int event_timed_destructor(void *ptr) -{ - struct timed_event *te = talloc_get_type(ptr, struct timed_event); - DLIST_REMOVE(te->event_ctx->timed_events, te); - return 0; -} - -/* - add a timed event - return NULL on failure (memory allocation error) -*/ -struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx, - struct timeval next_event, - event_timed_handler_t handler, - void *private) -{ - struct timed_event *te, *e; - - e = talloc(mem_ctx?mem_ctx:ev, struct timed_event); - if (e == NULL) return NULL; - - e->event_ctx = ev; - e->next_event = next_event; - e->handler = handler; - e->private = private; - - /* keep the list ordered */ - if (ev->timed_events == NULL || - timeval_compare(&e->next_event, &ev->timed_events->next_event) > 0) { - DLIST_ADD(ev->timed_events, e); - } else { - for (te=ev->timed_events;te && te->next;te=te->next) { - if (!timeval_is_zero(&te->next_event) && - timeval_compare(&te->next_event, &e->next_event) < 0) { - break; - } - } - DLIST_ADD_AFTER(ev->timed_events, e, te); - } - - talloc_set_destructor(e, event_timed_destructor); - - return e; -} - -/* - a timer has gone off - call it -*/ -static void event_loop_timer(struct event_context *ev) -{ - struct timeval t = timeval_current(); - struct timed_event *te = ev->timed_events; - - te->next_event = timeval_zero(); - - te->handler(ev, te, t, te->private); - - /* note the care taken to prevent referencing a event - that could have been freed by the handler */ - if (ev->timed_events && timeval_is_zero(&ev->timed_events->next_event)) { - talloc_free(ev->timed_events); - } -} - -#if WITH_EPOLL -/* - event loop handling using epoll -*/ -static int event_loop_epoll(struct event_context *ev, struct timeval *tvalp) -{ - int ret, i; - const int maxevents = 8; - struct epoll_event events[maxevents]; - uint32_t destruction_count = ev->destruction_count; - int timeout = -1; - - if (tvalp) { - timeout = (tvalp->tv_usec / 1000) + (tvalp->tv_sec*1000); - } - - ret = epoll_wait(ev->epoll_fd, events, maxevents, timeout); - - if (ret == -1 && errno != EINTR) { - epoll_fallback_to_select(ev, "epoll_wait() failed"); - return -1; - } - - if (ret == 0 && tvalp) { - event_loop_timer(ev); - return 0; - } - - for (i=0;ihandler(ev, fde, flags, fde->private); - if (destruction_count != ev->destruction_count) { - break; - } - } - } - - return 0; -} -#endif - -/* - event loop handling using select() -*/ -static int event_loop_select(struct event_context *ev, struct timeval *tvalp) -{ - fd_set r_fds, w_fds; - int selrtn; - uint32_t destruction_count = ev->destruction_count; - struct fd_event *fe; - - /* we maybe need to recalculate the maxfd */ - if (ev->maxfd == EVENT_INVALID_MAXFD) { - calc_maxfd(ev); - } - - FD_ZERO(&r_fds); - FD_ZERO(&w_fds); - - /* setup any fd events */ - for (fe=ev->fd_events; fe; ) { - struct fd_event *next = fe->next; - if (fe->flags & EVENT_FD_READ) { - FD_SET(fe->fd, &r_fds); - } - if (fe->flags & EVENT_FD_WRITE) { - FD_SET(fe->fd, &w_fds); - } - fe = next; - } - - selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); - - if (selrtn == -1 && errno == EBADF) { - /* the socket is dead! this should never - happen as the socket should have first been - made readable and that should have removed - the event, so this must be a bug. This is a - fatal error. */ - DEBUG(0,("ERROR: EBADF on event_loop_once\n")); - ev->exit_code = EBADF; - return -1; - } - - if (selrtn == 0 && tvalp) { - event_loop_timer(ev); - return 0; - } - - if (selrtn > 0) { - /* at least one file descriptor is ready - check - which ones and call the handler, being careful to allow - the handler to remove itself when called */ - for (fe=ev->fd_events; fe; fe=fe->next) { - uint16_t flags = 0; - if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ; - if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE; - if (flags) { - fe->handler(ev, fe, flags, fe->private); - if (destruction_count != ev->destruction_count) { - break; - } - } - } - } - - return 0; -} - -/* - do a single event loop using the events defined in ev -*/ -int event_loop_once(struct event_context *ev) -{ - struct timeval tval, *tvalp; - - tvalp = NULL; - - /* work out the right timeout for all timed events */ - if (ev->timed_events) { - struct timeval t = timeval_current(); - tval = timeval_diff(&ev->timed_events->next_event, &t); - tvalp = &tval; - if (timeval_is_zero(tvalp)) { - event_loop_timer(ev); - return 0; - } - } - -#if WITH_EPOLL - if (ev->epoll_fd != -1) { - if (event_loop_epoll(ev, tvalp) == 0) { - return 0; - } - } -#endif - - return event_loop_select(ev, tvalp); -} - -/* - go into an event loop using the events defined in ev this function - will return with the specified code if one of the handlers calls - event_loop_exit() - - also return (with code 0) if all fd events are removed -*/ -int event_loop_wait(struct event_context *ev) -{ - ev->exit_code = 0; - ev->maxfd = EVENT_INVALID_MAXFD; - - while (ev->fd_events && ev->exit_code == 0) { - if (event_loop_once(ev) != 0) { - break; - } - } - - return ev->exit_code; -} diff --git a/source4/lib/events/config.m4 b/source4/lib/events/config.m4 new file mode 100644 index 0000000000..6e4095d5b8 --- /dev/null +++ b/source4/lib/events/config.m4 @@ -0,0 +1,2 @@ +AC_CHECK_HEADERS(sys/epoll.h) +AC_CHECK_FUNCS(epoll_create) diff --git a/source4/lib/events/config.mk b/source4/lib/events/config.mk new file mode 100644 index 0000000000..c6f66b1c51 --- /dev/null +++ b/source4/lib/events/config.mk @@ -0,0 +1,8 @@ +############################## +# Start SUBSYSTEM LIBEVENTS +[SUBSYSTEM::LIBEVENTS] +NOPROTO = YES +INIT_OBJ_FILES = lib/events/events.o +REQUIRED_SUBSYSTEMS = LIBTALLOC +# End SUBSYSTEM LIBEVENTS +############################## diff --git a/source4/lib/events/events.c b/source4/lib/events/events.c new file mode 100644 index 0000000000..a852df68eb --- /dev/null +++ b/source4/lib/events/events.c @@ -0,0 +1,520 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003 + + 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 + (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. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + PLEASE READ THIS BEFORE MODIFYING! + + This module is a general abstraction for the main select loop and + event handling. Do not ever put any localised hacks in here, instead + register one of the possible event types and implement that event + somewhere else. + + There are 2 types of event handling that are handled in this module: + + 1) a file descriptor becoming readable or writeable. This is mostly + used for network sockets, but can be used for any type of file + descriptor. You may only register one handler for each file + descriptor/io combination or you will get unpredictable results + (this means that you can have a handler for read events, and a + separate handler for write events, but not two handlers that are + both handling read events) + + 2) a timed event. You can register an event that happens at a + specific time. You can register as many of these as you + like. They are single shot - add a new timed event in the event + handler to get another event. + + To setup a set of events you first need to create a event_context + structure using the function event_context_init(); This returns a + 'struct event_context' that you use in all subsequent calls. + + After that you can add/remove events that you are interested in + using event_add_*() and talloc_free() + + Finally, you call event_loop_wait() to block waiting for one of the + events to occor. In normal operation event_loop_wait() will loop + forever. + +*/ + +#include "includes.h" +#include "system/time.h" +#include "system/select.h" +#include "dlinklist.h" +#include "lib/events/events.h" + +/* use epoll if it is available */ +#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_EPOLL_H) +#define WITH_EPOLL 1 +#endif + +#if WITH_EPOLL +#include +#endif + +struct event_context { + /* list of filedescriptor events */ + struct fd_event { + struct event_context *event_ctx; + struct fd_event *next, *prev; + int fd; + uint16_t flags; /* see EVENT_FD_* flags */ + event_fd_handler_t handler; + void *private; + } *fd_events; + + /* list of timed events */ + struct timed_event { + struct event_context *event_ctx; + struct timed_event *next, *prev; + struct timeval next_event; + event_timed_handler_t handler; + void *private; + } *timed_events; + + /* the maximum file descriptor number in fd_events */ + int maxfd; + + /* information for exiting from the event loop */ + int exit_code; + + /* this is changed by the destructors for the fd event + type. It is used to detect event destruction by event + handlers, which means the code that is calling the event + handler needs to assume that the linked list is no longer + valid + */ + uint32_t destruction_count; + +#if WITH_EPOLL + /* when using epoll this is the handle from epoll_create */ + int epoll_fd; +#endif +}; + + +/* + create a event_context structure. This must be the first events + call, and all subsequent calls pass this event_context as the first + element. Event handlers also receive this as their first argument. +*/ +struct event_context *event_context_init(TALLOC_CTX *mem_ctx) +{ + struct event_context *ev; + + ev = talloc_zero(mem_ctx, struct event_context); + if (!ev) return NULL; + +#if WITH_EPOLL + ev->epoll_fd = epoll_create(64); +#endif + + return ev; +} + + +/* + recalculate the maxfd +*/ +static void calc_maxfd(struct event_context *ev) +{ + struct fd_event *e; + ev->maxfd = 0; + for (e=ev->fd_events; e; e=e->next) { + if (e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + } +} + + +/* to mark the ev->maxfd invalid + * this means we need to recalculate it + */ +#define EVENT_INVALID_MAXFD (-1) + + +#if WITH_EPOLL +/* + called when a epoll call fails, and we should fallback + to using select +*/ +static void epoll_fallback_to_select(struct event_context *ev, const char *reason) +{ + DEBUG(0,("%s - using select() - %s\n", reason, strerror(errno))); + close(ev->epoll_fd); + ev->epoll_fd = -1; +} +#endif + + +#if WITH_EPOLL +/* + map from EVENT_FD_* to EPOLLIN/EPOLLOUT +*/ +static uint32_t epoll_map_flags(uint16_t flags) +{ + uint32_t ret = 0; + if (flags & EVENT_FD_READ) ret |= EPOLLIN; + if (flags & EVENT_FD_WRITE) ret |= EPOLLOUT; + return ret; +} +#endif + +/* + destroy an fd_event +*/ +static int event_fd_destructor(void *ptr) +{ + struct fd_event *fde = talloc_get_type(ptr, struct fd_event); + struct event_context *ev = fde->event_ctx; + + if (ev->maxfd == fde->fd) { + ev->maxfd = EVENT_INVALID_MAXFD; + } + DLIST_REMOVE(ev->fd_events, fde); + ev->destruction_count++; +#if WITH_EPOLL + if (ev->epoll_fd != -1) { + struct epoll_event event; + ZERO_STRUCT(event); + event.events = epoll_map_flags(fde->flags); + event.data.ptr = fde; + if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fde->fd, &event) != 0) { + epoll_fallback_to_select(ev, "EPOLL_CTL_DEL failed"); + } + } +#endif + return 0; +} + +/* + add a fd based event + return NULL on failure (memory allocation error) +*/ +struct fd_event *event_add_fd(struct event_context *ev, TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, event_fd_handler_t handler, + void *private) +{ + struct fd_event *e = talloc(ev, struct fd_event); + if (!e) return NULL; + + e->event_ctx = ev; + e->fd = fd; + e->flags = flags; + e->handler = handler; + e->private = private; + + DLIST_ADD(ev->fd_events, e); + + if (e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + + talloc_set_destructor(e, event_fd_destructor); + if (mem_ctx) { + talloc_steal(mem_ctx, e); + } + +#if WITH_EPOLL + if (ev->epoll_fd != -1) { + struct epoll_event event; + ZERO_STRUCT(event); + event.events = epoll_map_flags(flags); + event.data.ptr = e; + if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_ADD, e->fd, &event) != 0) { + epoll_fallback_to_select(ev, "EPOLL_CTL_ADD failed"); + } + } +#endif + + return e; +} + + +/* + return the fd event flags +*/ +uint16_t event_fd_flags(struct fd_event *fde) +{ + return fde?fde->flags:0; +} + +/* + set the fd event flags +*/ +void event_fd_setflags(struct fd_event *fde, uint16_t flags) +{ +#if WITH_EPOLL + struct event_context *ev; + if (fde == NULL) return; + ev = fde->event_ctx; + if (ev->epoll_fd != -1) { + struct epoll_event event; + ZERO_STRUCT(event); + event.events = epoll_map_flags(flags); + event.data.ptr = fde; + if (epoll_ctl(ev->epoll_fd, EPOLL_CTL_MOD, fde->fd, &event) != 0) { + epoll_fallback_to_select(ev, "EPOLL_CTL_MOD failed"); + } + } +#endif + if (fde) { + fde->flags = flags; + } +} + +/* + destroy a timed event +*/ +static int event_timed_destructor(void *ptr) +{ + struct timed_event *te = talloc_get_type(ptr, struct timed_event); + DLIST_REMOVE(te->event_ctx->timed_events, te); + return 0; +} + +/* + add a timed event + return NULL on failure (memory allocation error) +*/ +struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx, + struct timeval next_event, + event_timed_handler_t handler, + void *private) +{ + struct timed_event *te, *e; + + e = talloc(mem_ctx?mem_ctx:ev, struct timed_event); + if (e == NULL) return NULL; + + e->event_ctx = ev; + e->next_event = next_event; + e->handler = handler; + e->private = private; + + /* keep the list ordered */ + if (ev->timed_events == NULL || + timeval_compare(&e->next_event, &ev->timed_events->next_event) > 0) { + DLIST_ADD(ev->timed_events, e); + } else { + for (te=ev->timed_events;te && te->next;te=te->next) { + if (!timeval_is_zero(&te->next_event) && + timeval_compare(&te->next_event, &e->next_event) < 0) { + break; + } + } + DLIST_ADD_AFTER(ev->timed_events, e, te); + } + + talloc_set_destructor(e, event_timed_destructor); + + return e; +} + +/* + a timer has gone off - call it +*/ +static void event_loop_timer(struct event_context *ev) +{ + struct timeval t = timeval_current(); + struct timed_event *te = ev->timed_events; + + te->next_event = timeval_zero(); + + te->handler(ev, te, t, te->private); + + /* note the care taken to prevent referencing a event + that could have been freed by the handler */ + if (ev->timed_events && timeval_is_zero(&ev->timed_events->next_event)) { + talloc_free(ev->timed_events); + } +} + +#if WITH_EPOLL +/* + event loop handling using epoll +*/ +static int event_loop_epoll(struct event_context *ev, struct timeval *tvalp) +{ + int ret, i; + const int maxevents = 8; + struct epoll_event events[maxevents]; + uint32_t destruction_count = ev->destruction_count; + int timeout = -1; + + if (tvalp) { + timeout = (tvalp->tv_usec / 1000) + (tvalp->tv_sec*1000); + } + + ret = epoll_wait(ev->epoll_fd, events, maxevents, timeout); + + if (ret == -1 && errno != EINTR) { + epoll_fallback_to_select(ev, "epoll_wait() failed"); + return -1; + } + + if (ret == 0 && tvalp) { + event_loop_timer(ev); + return 0; + } + + for (i=0;ihandler(ev, fde, flags, fde->private); + if (destruction_count != ev->destruction_count) { + break; + } + } + } + + return 0; +} +#endif + +/* + event loop handling using select() +*/ +static int event_loop_select(struct event_context *ev, struct timeval *tvalp) +{ + fd_set r_fds, w_fds; + int selrtn; + uint32_t destruction_count = ev->destruction_count; + struct fd_event *fe; + + /* we maybe need to recalculate the maxfd */ + if (ev->maxfd == EVENT_INVALID_MAXFD) { + calc_maxfd(ev); + } + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + /* setup any fd events */ + for (fe=ev->fd_events; fe; ) { + struct fd_event *next = fe->next; + if (fe->flags & EVENT_FD_READ) { + FD_SET(fe->fd, &r_fds); + } + if (fe->flags & EVENT_FD_WRITE) { + FD_SET(fe->fd, &w_fds); + } + fe = next; + } + + selrtn = select(ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + + if (selrtn == -1 && errno == EBADF) { + /* the socket is dead! this should never + happen as the socket should have first been + made readable and that should have removed + the event, so this must be a bug. This is a + fatal error. */ + DEBUG(0,("ERROR: EBADF on event_loop_once\n")); + ev->exit_code = EBADF; + return -1; + } + + if (selrtn == 0 && tvalp) { + event_loop_timer(ev); + return 0; + } + + if (selrtn > 0) { + /* at least one file descriptor is ready - check + which ones and call the handler, being careful to allow + the handler to remove itself when called */ + for (fe=ev->fd_events; fe; fe=fe->next) { + uint16_t flags = 0; + if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ; + if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE; + if (flags) { + fe->handler(ev, fe, flags, fe->private); + if (destruction_count != ev->destruction_count) { + break; + } + } + } + } + + return 0; +} + +/* + do a single event loop using the events defined in ev +*/ +int event_loop_once(struct event_context *ev) +{ + struct timeval tval, *tvalp; + + tvalp = NULL; + + /* work out the right timeout for all timed events */ + if (ev->timed_events) { + struct timeval t = timeval_current(); + tval = timeval_diff(&ev->timed_events->next_event, &t); + tvalp = &tval; + if (timeval_is_zero(tvalp)) { + event_loop_timer(ev); + return 0; + } + } + +#if WITH_EPOLL + if (ev->epoll_fd != -1) { + if (event_loop_epoll(ev, tvalp) == 0) { + return 0; + } + } +#endif + + return event_loop_select(ev, tvalp); +} + +/* + go into an event loop using the events defined in ev this function + will return with the specified code if one of the handlers calls + event_loop_exit() + + also return (with code 0) if all fd events are removed +*/ +int event_loop_wait(struct event_context *ev) +{ + ev->exit_code = 0; + ev->maxfd = EVENT_INVALID_MAXFD; + + while (ev->fd_events && ev->exit_code == 0) { + if (event_loop_once(ev) != 0) { + break; + } + } + + return ev->exit_code; +} diff --git a/source4/lib/events/events.h b/source4/lib/events/events.h new file mode 100644 index 0000000000..e3973c3c48 --- /dev/null +++ b/source4/lib/events/events.h @@ -0,0 +1,63 @@ +/* + Unix SMB/CIFS implementation. + + generalised event loop handling + + Copyright (C) Andrew Tridgell 2005 + + 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 + (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. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +struct event_context; +struct fd_event; +struct timed_event; + +/* event handler types */ +typedef void (*event_fd_handler_t)(struct event_context *, struct fd_event *, + uint16_t , void *); +typedef void (*event_timed_handler_t)(struct event_context *, struct timed_event *, + struct timeval , void *); + +struct event_context *event_context_init(TALLOC_CTX *mem_ctx); + +struct fd_event *event_add_fd(struct event_context *ev, TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, event_fd_handler_t handler, + void *private); + +struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ctx, + struct timeval next_event, + event_timed_handler_t handler, + void *private); + +int event_loop_once(struct event_context *ev); +int event_loop_wait(struct event_context *ev); + +uint16_t event_fd_flags(struct fd_event *fde); +void event_fd_setflags(struct fd_event *fde, uint16_t flags); + +/* bits for file descriptor event flags */ +#define EVENT_FD_READ 1 +#define EVENT_FD_WRITE 2 + +#define EVENT_FD_WRITEABLE(fde) \ + event_fd_setflags(fde, event_fd_flags(fde) | EVENT_FD_WRITE) +#define EVENT_FD_READABLE(fde) \ + event_fd_setflags(fde, event_fd_flags(fde) | EVENT_FD_READ) + +#define EVENT_FD_NOT_WRITEABLE(fde) \ + event_fd_setflags(fde, event_fd_flags(fde) & ~EVENT_FD_WRITE) +#define EVENT_FD_NOT_READABLE(fde) \ + event_fd_setflags(fde, event_fd_flags(fde) & ~EVENT_FD_WRITE) + diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c index 24205e5151..aab13ba8af 100644 --- a/source4/lib/messaging/messaging.c +++ b/source4/lib/messaging/messaging.c @@ -21,7 +21,7 @@ */ #include "includes.h" -#include "events.h" +#include "lib/events/events.h" #include "system/time.h" #include "messages.h" #include "dlinklist.h" -- cgit