X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=lib%2Ftevent%2Ftevent.c;h=3efc8d909d524e31be8fd0657125625045e60041;hb=5eb9cc551d9e488daef34ace132a6d7b2e43152c;hp=ea16fa1e071417fc61bdd25b22b8f62481bce6ad;hpb=5d19187d3d69fff5c9c7551782adc18b5669c263;p=metze%2Fsamba%2Fwip.git diff --git a/lib/tevent/tevent.c b/lib/tevent/tevent.c index ea16fa1e0714..3efc8d909d52 100644 --- a/lib/tevent/tevent.c +++ b/lib/tevent/tevent.c @@ -2,19 +2,24 @@ 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 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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, see . + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . */ /* @@ -54,6 +59,7 @@ */ #include "replace.h" #include "system/filesys.h" +#define TEVENT_DEPRECATED 1 #include "tevent.h" #include "tevent_internal.h" #include "tevent_util.h" @@ -82,7 +88,7 @@ bool tevent_register_backend(const char *name, const struct tevent_ops *ops) } } - e = talloc(talloc_autofree_context(), struct tevent_ops_list); + e = talloc(NULL, struct tevent_ops_list); if (e == NULL) return false; e->name = name; @@ -98,8 +104,7 @@ bool tevent_register_backend(const char *name, const struct tevent_ops *ops) void tevent_set_default_backend(const char *backend) { talloc_free(tevent_default_backend); - tevent_default_backend = talloc_strdup(talloc_autofree_context(), - backend); + tevent_default_backend = talloc_strdup(NULL, backend); } /* @@ -107,14 +112,46 @@ void tevent_set_default_backend(const char *backend) */ static void tevent_backend_init(void) { + static bool done; + + if (done) { + return; + } + + done = true; + tevent_select_init(); - tevent_standard_init(); + tevent_poll_init(); + tevent_poll_mt_init(); #ifdef HAVE_EPOLL tevent_epoll_init(); #endif -#ifdef HAVE_LINUX_AIO - tevent_aio_init(); +#ifdef HAVE_KQUEUE + tevent_kqueue_init(); #endif + tevent_standard_init(); +} + +_PRIVATE_ const struct tevent_ops *tevent_find_ops_byname(const char *name) +{ + struct tevent_ops_list *e; + + tevent_backend_init(); + + if (name == NULL) { + name = tevent_default_backend; + } + if (name == NULL) { + name = "standard"; + } + + for (e = tevent_backends; e != NULL; e = e->next) { + if (0 == strcmp(e->name, name)) { + return e->ops; + } + } + + return NULL; } /* @@ -140,10 +177,13 @@ int tevent_common_context_destructor(struct tevent_context *ev) { struct tevent_fd *fd, *fn; struct tevent_timer *te, *tn; + struct tevent_immediate *ie, *in; struct tevent_signal *se, *sn; if (ev->pipe_fde) { talloc_free(ev->pipe_fde); + close(ev->pipe_fds[0]); + close(ev->pipe_fds[1]); ev->pipe_fde = NULL; } @@ -153,18 +193,44 @@ int tevent_common_context_destructor(struct tevent_context *ev) DLIST_REMOVE(ev->fd_events, fd); } + ev->last_zero_timer = NULL; for (te = ev->timer_events; te; te = tn) { tn = te->next; te->event_ctx = NULL; DLIST_REMOVE(ev->timer_events, te); } + for (ie = ev->immediate_events; ie; ie = in) { + in = ie->next; + ie->event_ctx = NULL; + ie->cancel_fn = NULL; + DLIST_REMOVE(ev->immediate_events, ie); + } + for (se = ev->signal_events; se; se = sn) { sn = se->next; se->event_ctx = NULL; DLIST_REMOVE(ev->signal_events, se); + /* + * This is important, Otherwise signals + * are handled twice in child. eg, SIGHUP. + * one added in parent, and another one in + * the child. -- BoYang + */ + tevent_cleanup_pending_signal_handlers(se); } + /* removing nesting hook or we get an abort when nesting is + * not allowed. -- SSS + * Note that we need to leave the allowed flag at its current + * value, otherwise the use in tevent_re_initialise() will + * leave the event context with allowed forced to false, which + * will break users that expect nesting to be allowed + */ + ev->nesting.level = 0; + ev->nesting.hook_fn = NULL; + ev->nesting.hook_private = NULL; + return 0; } @@ -179,8 +245,9 @@ int tevent_common_context_destructor(struct tevent_context *ev) NOTE: use tevent_context_init() inside of samba! */ -static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, - const struct tevent_ops *ops) +struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, + const struct tevent_ops *ops, + void *additional_data) { struct tevent_context *ev; int ret; @@ -191,6 +258,7 @@ static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, talloc_set_destructor(ev, tevent_common_context_destructor); ev->ops = ops; + ev->additional_data = additional_data; ret = ev->ops->context_init(ev); if (ret != 0) { @@ -209,23 +277,14 @@ static struct tevent_context *tevent_context_init_ops(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, const char *name) { - struct tevent_ops_list *e; - - tevent_backend_init(); + const struct tevent_ops *ops; - if (name == NULL) { - name = tevent_default_backend; - } - if (name == NULL) { - name = "standard"; + ops = tevent_find_ops_byname(name); + if (ops == NULL) { + return NULL; } - for (e=tevent_backends;e;e=e->next) { - if (strcmp(name, e->name) == 0) { - return tevent_context_init_ops(mem_ctx, e->ops); - } - } - return NULL; + return tevent_context_init_ops(mem_ctx, ops, NULL); } @@ -242,9 +301,6 @@ struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx) /* add a fd based event return NULL on failure (memory allocation error) - - if flags contains TEVENT_FD_AUTOCLOSE then the fd will be closed when - the returned fd_event context is freed */ struct tevent_fd *_tevent_add_fd(struct tevent_context *ev, TALLOC_CTX *mem_ctx, @@ -259,22 +315,6 @@ struct tevent_fd *_tevent_add_fd(struct tevent_context *ev, handler_name, location); } -/* - add a disk aio event -*/ -struct tevent_aio *_tevent_add_aio(struct tevent_context *ev, - TALLOC_CTX *mem_ctx, - struct iocb *iocb, - tevent_aio_handler_t handler, - void *private_data, - const char *handler_name, - const char *location) -{ - if (ev->ops->add_aio == NULL) return NULL; - return ev->ops->add_aio(ev, mem_ctx, iocb, handler, private_data, - handler_name, location); -} - /* set a close function on the fd event */ @@ -319,6 +359,33 @@ void tevent_fd_set_flags(struct tevent_fd *fde, uint16_t flags) fde->event_ctx->ops->set_fd_flags(fde, flags); } +bool tevent_signal_support(struct tevent_context *ev) +{ + if (ev->ops->add_signal) { + return true; + } + return false; +} + +static void (*tevent_abort_fn)(const char *reason); + +void tevent_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + tevent_abort_fn = abort_fn; +} + +static void tevent_abort(struct tevent_context *ev, const char *reason) +{ + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "abort: %s\n", reason); + + if (!tevent_abort_fn) { + abort(); + } + + tevent_abort_fn(reason); +} + /* add a timer event return NULL on failure @@ -335,6 +402,46 @@ struct tevent_timer *_tevent_add_timer(struct tevent_context *ev, handler_name, location); } +/* + allocate an immediate event + return NULL on failure (memory allocation error) +*/ +struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx, + const char *location) +{ + struct tevent_immediate *im; + + im = talloc(mem_ctx, struct tevent_immediate); + if (im == NULL) return NULL; + + im->prev = NULL; + im->next = NULL; + im->event_ctx = NULL; + im->create_location = location; + im->handler = NULL; + im->private_data = NULL; + im->handler_name = NULL; + im->schedule_location = NULL; + im->cancel_fn = NULL; + im->additional_data = NULL; + + return im; +} + +/* + schedule an immediate event +*/ +void _tevent_schedule_immediate(struct tevent_immediate *im, + struct tevent_context *ev, + tevent_immediate_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + ev->ops->schedule_immediate(im, ev, handler, private_data, + handler_name, location); +} + /* add a signal event @@ -355,18 +462,211 @@ struct tevent_signal *_tevent_add_signal(struct tevent_context *ev, handler_name, location); } +void tevent_loop_allow_nesting(struct tevent_context *ev) +{ + ev->nesting.allowed = true; +} + +void tevent_loop_set_nesting_hook(struct tevent_context *ev, + tevent_nesting_hook hook, + void *private_data) +{ + if (ev->nesting.hook_fn && + (ev->nesting.hook_fn != hook || + ev->nesting.hook_private != private_data)) { + /* the way the nesting hook code is currently written + we cannot support two different nesting hooks at the + same time. */ + tevent_abort(ev, "tevent: Violation of nesting hook rules\n"); + } + ev->nesting.hook_fn = hook; + ev->nesting.hook_private = private_data; +} + +static void tevent_abort_nesting(struct tevent_context *ev, const char *location) +{ + const char *reason; + + reason = talloc_asprintf(NULL, "tevent_loop_once() nesting at %s", + location); + if (!reason) { + reason = "tevent_loop_once() nesting"; + } + + tevent_abort(ev, reason); +} + /* do a single event loop using the events defined in ev */ -int tevent_loop_once(struct tevent_context *ev) +int _tevent_loop_once(struct tevent_context *ev, const char *location) { - return ev->ops->loop_once(ev); + int ret; + void *nesting_stack_ptr = NULL; + + ev->nesting.level++; + + if (ev->nesting.level > 1) { + if (!ev->nesting.allowed) { + tevent_abort_nesting(ev, location); + errno = ELOOP; + return -1; + } + } + if (ev->nesting.level > 0) { + if (ev->nesting.hook_fn) { + int ret2; + ret2 = ev->nesting.hook_fn(ev, + ev->nesting.hook_private, + ev->nesting.level, + true, + (void *)&nesting_stack_ptr, + location); + if (ret2 != 0) { + ret = ret2; + goto done; + } + } + } + + tevent_trace_point_callback(ev, TEVENT_TRACE_BEFORE_LOOP_ONCE); + ret = ev->ops->loop_once(ev, location); + tevent_trace_point_callback(ev, TEVENT_TRACE_AFTER_LOOP_ONCE); + + if (ev->nesting.level > 0) { + if (ev->nesting.hook_fn) { + int ret2; + ret2 = ev->nesting.hook_fn(ev, + ev->nesting.hook_private, + ev->nesting.level, + false, + (void *)&nesting_stack_ptr, + location); + if (ret2 != 0) { + ret = ret2; + goto done; + } + } + } + +done: + ev->nesting.level--; + return ret; +} + +/* + this is a performance optimization for the samba4 nested event loop problems +*/ +int _tevent_loop_until(struct tevent_context *ev, + bool (*finished)(void *private_data), + void *private_data, + const char *location) +{ + int ret = 0; + void *nesting_stack_ptr = NULL; + + ev->nesting.level++; + + if (ev->nesting.level > 1) { + if (!ev->nesting.allowed) { + tevent_abort_nesting(ev, location); + errno = ELOOP; + return -1; + } + } + if (ev->nesting.level > 0) { + if (ev->nesting.hook_fn) { + int ret2; + ret2 = ev->nesting.hook_fn(ev, + ev->nesting.hook_private, + ev->nesting.level, + true, + (void *)&nesting_stack_ptr, + location); + if (ret2 != 0) { + ret = ret2; + goto done; + } + } + } + + while (!finished(private_data)) { + tevent_trace_point_callback(ev, TEVENT_TRACE_BEFORE_LOOP_ONCE); + ret = ev->ops->loop_once(ev, location); + tevent_trace_point_callback(ev, TEVENT_TRACE_AFTER_LOOP_ONCE); + if (ret != 0) { + break; + } + } + + if (ev->nesting.level > 0) { + if (ev->nesting.hook_fn) { + int ret2; + ret2 = ev->nesting.hook_fn(ev, + ev->nesting.hook_private, + ev->nesting.level, + false, + (void *)&nesting_stack_ptr, + location); + if (ret2 != 0) { + ret = ret2; + goto done; + } + } + } + +done: + ev->nesting.level--; + return ret; +} + +/* + return on failure or (with 0) if all fd events are removed +*/ +int tevent_common_loop_wait(struct tevent_context *ev, + const char *location) +{ + /* + * loop as long as we have events pending + */ + while (ev->fd_events || + ev->timer_events || + ev->immediate_events || + ev->signal_events) { + int ret; + ret = _tevent_loop_once(ev, location); + if (ret != 0) { + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "_tevent_loop_once() failed: %d - %s\n", + ret, strerror(errno)); + return ret; + } + } + + tevent_debug(ev, TEVENT_DEBUG_WARNING, + "tevent_common_loop_wait() out of events\n"); + return 0; } /* return on failure or (with 0) if all fd events are removed */ -int tevent_loop_wait(struct tevent_context *ev) +int _tevent_loop_wait(struct tevent_context *ev, const char *location) { - return ev->ops->loop_wait(ev); + return ev->ops->loop_wait(ev, location); +} + + +/* + re-initialise a tevent context. This leaves you with the same + event context, but all events are wiped and the structure is + re-initialised. This is most useful after a fork() + + zero is returned on success, non-zero on failure +*/ +int tevent_re_initialise(struct tevent_context *ev) +{ + tevent_common_context_destructor(ev); + + return ev->ops->context_init(ev); }