diff options
-rw-r--r-- | lib/tevent/tevent_standard.c | 186 |
1 files changed, 180 insertions, 6 deletions
diff --git a/lib/tevent/tevent_standard.c b/lib/tevent/tevent_standard.c index 938b223572..0c72226558 100644 --- a/lib/tevent/tevent_standard.c +++ b/lib/tevent/tevent_standard.c @@ -2,7 +2,8 @@ Unix SMB/CIFS implementation. main select loop and event handling Copyright (C) Andrew Tridgell 2003-2005 - Copyright (C) Stefan Metzmacher 2005-2009 + Copyright (C) Stefan Metzmacher 2005-2013 + Copyright (C) Jeremy Allison 2013 ** NOTE! The following LGPL license applies to the tevent ** library. This does NOT imply that all of Samba is released @@ -26,18 +27,17 @@ This is SAMBA's default event loop code - we try to use epoll if configure detected support for it - otherwise we use select() + otherwise we use poll() - if epoll is broken on the system or the kernel doesn't support it - at runtime we fallback to select() + at runtime we fallback to poll() */ #include "replace.h" -#include "system/filesys.h" -#include "system/select.h" #include "tevent.h" #include "tevent_util.h" #include "tevent_internal.h" +#if 0 struct std_event_context { /* a pointer back to the generic event_context */ struct tevent_context *ev; @@ -589,10 +589,184 @@ static const struct tevent_ops std_event_ops = { .loop_once = std_event_loop_once, .loop_wait = tevent_common_loop_wait, }; +#endif + +struct std_event_glue { + const struct tevent_ops *epoll_ops; + const struct tevent_ops *poll_ops; + struct tevent_ops *glue_ops; + bool fallback_replay; +}; + +static int std_event_context_init(struct tevent_context *ev); + +static const struct tevent_ops std_event_ops = { + .context_init = std_event_context_init, +}; + +/* + If this function gets called. epoll failed at runtime. + Move us to using poll instead. If we return false here, + caller should abort(). +*/ +static bool std_fallback_to_poll(struct tevent_context *ev, bool replay) +{ + void *glue_ptr = talloc_parent(ev->ops); + struct std_event_glue *glue = + talloc_get_type_abort(glue_ptr, + struct std_event_glue); + int ret; + struct tevent_fd *fde; + struct tevent_fd *fde_next; + + glue->fallback_replay = replay; + + /* First switch all the ops to poll. */ + glue->epoll_ops = NULL; + TALLOC_FREE(ev->additional_data); + + /* + * Set custom_ops the same as poll. + */ + *glue->glue_ops = *glue->poll_ops; + glue->glue_ops->context_init = std_event_context_init; + /* Next initialize the poll backend. */ + ret = glue->poll_ops->context_init(ev); + if (ret != 0) { + return false; + } + + /* + * Now we have to change all the existing file descriptor + * events from the epoll backend to the poll backend. + */ + for (fde = ev->fd_events; fde; fde = fde_next) { + /* + * We must remove this fde off the ev->fd_events list. + */ + fde_next = fde->next; + + /* Remove from the ev->fd_events list. */ + DLIST_REMOVE(ev->fd_events, fde); + + /* Re-add this event as a poll backend event. */ + tevent_poll_event_add_fd_internal(ev, fde); + } + + return true; +} + +static int std_event_loop_once(struct tevent_context *ev, const char *location) +{ + void *glue_ptr = talloc_parent(ev->ops); + struct std_event_glue *glue = + talloc_get_type_abort(glue_ptr, + struct std_event_glue); + int ret; + + ret = glue->epoll_ops->loop_once(ev, location); + if (glue->epoll_ops != NULL) { + /* No fallback */ + return ret; + } + + if (!glue->fallback_replay) { + /* + * The problem happened while modifying an event. + * An event handler was triggered in this case + * and there is no need to call loop_once() again. + */ + return ret; + } + + return glue->poll_ops->loop_once(ev, location); +} + +/* + Initialize the epoll backend and allow it to call a + switch function if epoll fails at runtime. +*/ +static int std_event_context_init(struct tevent_context *ev) +{ + struct std_event_glue *glue; + int ret; + + /* + * If this is the first initialization + * we need to set up the allocated ops + * pointers. + */ + + if (ev->ops == &std_event_ops) { + glue = talloc_zero(ev, struct std_event_glue); + if (glue == NULL) { + return -1; + } + + glue->epoll_ops = tevent_find_ops_byname("epoll"); + + glue->poll_ops = tevent_find_ops_byname("poll"); + if (glue->poll_ops == NULL) { + return -1; + } + + /* + * Allocate space for our custom ops. + * Allocate as a child of our epoll_ops pointer + * so we can easily get to it using talloc_parent. + */ + glue->glue_ops = talloc_zero(glue, struct tevent_ops); + if (glue->glue_ops == NULL) { + talloc_free(glue); + return -1; + } + + ev->ops = glue->glue_ops; + } else { + void *glue_ptr = talloc_parent(ev->ops); + glue = talloc_get_type_abort(glue_ptr, struct std_event_glue); + } + + if (glue->epoll_ops != NULL) { + /* + * Set custom_ops the same as epoll, + * except re-init using std_event_context_init() + * and use std_event_loop_once() to add the + * ability to fallback to a poll backend on + * epoll runtime error. + */ + *glue->glue_ops = *glue->epoll_ops; + glue->glue_ops->context_init = std_event_context_init; + glue->glue_ops->loop_once = std_event_loop_once; + + ret = glue->epoll_ops->context_init(ev); + if (ret == -1) { + goto fallback; + } +#ifdef HAVE_EPOLL + if (!tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll)) { + TALLOC_FREE(ev->additional_data); + goto fallback; + } +#endif + + return ret; + } + +fallback: + glue->epoll_ops = NULL; + + /* + * Set custom_ops the same as poll. + */ + *glue->glue_ops = *glue->poll_ops; + glue->glue_ops->context_init = std_event_context_init; + + return glue->poll_ops->context_init(ev); +} _PRIVATE_ bool tevent_standard_init(void) { return tevent_register_backend("standard", &std_event_ops); } - |