From 336ae458e9f648625f8fe438bf1dc4c8f7e616a7 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 21 Jan 2007 08:23:14 +0000 Subject: r20928: added signal events to lib/events Jeremy asked for this to allow Samba3 to use the Samba4 events library see torture/local/event.c for an example (This used to be commit 7e105482ff9a3da6b4708ff99a64f1881614fc5f) --- source4/lib/events/config.mk | 2 +- source4/lib/events/events.c | 12 +++ source4/lib/events/events.h | 10 ++- source4/lib/events/events_aio.c | 10 +++ source4/lib/events/events_epoll.c | 12 +++ source4/lib/events/events_internal.h | 28 +++++++ source4/lib/events/events_liboop.c | 1 + source4/lib/events/events_select.c | 12 +++ source4/lib/events/events_signal.c | 153 +++++++++++++++++++++++++++++++++++ source4/lib/events/events_standard.c | 23 ++++++ 10 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 source4/lib/events/events_signal.c diff --git a/source4/lib/events/config.mk b/source4/lib/events/config.mk index 5830ca38f4..0a1a3bf0f9 100644 --- a/source4/lib/events/config.mk +++ b/source4/lib/events/config.mk @@ -31,7 +31,7 @@ INIT_FUNCTION = events_standard_init ############################## # Start SUBSYSTEM LIBEVENTS [SUBSYSTEM::LIBEVENTS] -OBJ_FILES = events.o events_timed.o +OBJ_FILES = events.o events_timed.o events_signal.o PUBLIC_DEPENDENCIES = LIBTALLOC # End SUBSYSTEM LIBEVENTS ############################## diff --git a/source4/lib/events/events.c b/source4/lib/events/events.c index 6159ea147f..38f2352f3b 100644 --- a/source4/lib/events/events.c +++ b/source4/lib/events/events.c @@ -239,6 +239,18 @@ struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ct return ev->ops->add_timed(ev, mem_ctx, next_event, handler, private_data); } +/* + add a signal event + return NULL on failure +*/ +struct timed_event *event_add_signal(struct event_context *ev, TALLOC_CTX *mem_ctx, + int signum, + event_signal_handler_t handler, + void *private_data) +{ + return ev->ops->add_signal(ev, mem_ctx, signum, handler, private_data); +} + /* do a single event loop using the events defined in ev */ diff --git a/source4/lib/events/events.h b/source4/lib/events/events.h index 62f1d8e9df..eee7a5e470 100644 --- a/source4/lib/events/events.h +++ b/source4/lib/events/events.h @@ -28,14 +28,17 @@ struct event_ops; struct fd_event; struct timed_event; struct aio_event; +struct signal_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 *); +typedef void (*event_signal_handler_t)(struct event_context *, struct signal_event *, + int , int, void *); typedef void (*event_aio_handler_t)(struct event_context *, struct aio_event *, - int , void *); + int, void *); struct event_context *event_context_init(TALLOC_CTX *mem_ctx); struct event_context *event_context_init_byname(TALLOC_CTX *mem_ctx, const char *name); @@ -50,6 +53,11 @@ struct timed_event *event_add_timed(struct event_context *ev, TALLOC_CTX *mem_ct event_timed_handler_t handler, void *private); +struct signal_event *event_add_signal(struct event_context *ev, TALLOC_CTX *mem_ctx, + int signum, + event_signal_handler_t handler, + void *private); + struct iocb; struct aio_event *event_add_aio(struct event_context *ev, TALLOC_CTX *mem_ctx, diff --git a/source4/lib/events/events_aio.c b/source4/lib/events/events_aio.c index 6d9dd6d753..327b36bcba 100644 --- a/source4/lib/events/events_aio.c +++ b/source4/lib/events/events_aio.c @@ -225,6 +225,11 @@ static int aio_event_loop(struct aio_event_context *aio_ev, struct timeval *tval if (aio_ev->epoll_fd == -1) return -1; + if (aio_ev->ev->num_signal_handlers && + common_event_check_signal(aio_ev->ev)) { + return 0; + } + if (tvalp) { timeout.tv_sec = tvalp->tv_sec; timeout.tv_nsec = tvalp->tv_usec; @@ -236,7 +241,11 @@ static int aio_event_loop(struct aio_event_context *aio_ev, struct timeval *tval ret = io_getevents(aio_ev->ioctx, 1, 8, events, tvalp?&timeout:NULL); + if (ret == -EINTR) { + if (aio_ev->ev->num_signal_handlers) { + common_event_check_signal(aio_ev->ev); + } return 0; } @@ -488,6 +497,7 @@ static const struct event_ops aio_event_ops = { .get_fd_flags = aio_event_get_fd_flags, .set_fd_flags = aio_event_set_fd_flags, .add_timed = common_event_add_timed, + .add_signal = common_event_add_signal, .loop_once = aio_event_loop_once, .loop_wait = aio_event_loop_wait, }; diff --git a/source4/lib/events/events_epoll.c b/source4/lib/events/events_epoll.c index f69b5bc6f4..efcbb97f0b 100644 --- a/source4/lib/events/events_epoll.c +++ b/source4/lib/events/events_epoll.c @@ -214,8 +214,19 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000); } + if (epoll_ev->ev->num_signal_handlers && + common_event_check_signal(epoll_ev->ev)) { + return 0; + } + ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout); + if (ret == -1 && errno == EINTR && epoll_ev->ev->num_signal_handlers) { + if (common_event_check_signal(epoll_ev->ev)) { + return 0; + } + } + if (ret == -1 && errno != EINTR) { epoll_fallback_to_select(epoll_ev, "epoll_wait() failed"); return -1; @@ -397,6 +408,7 @@ static const struct event_ops epoll_event_ops = { .get_fd_flags = epoll_event_get_fd_flags, .set_fd_flags = epoll_event_set_fd_flags, .add_timed = common_event_add_timed, + .add_signal = common_event_add_signal, .loop_once = epoll_event_loop_once, .loop_wait = epoll_event_loop_wait, }; diff --git a/source4/lib/events/events_internal.h b/source4/lib/events/events_internal.h index dc321769e0..0b8de12a35 100644 --- a/source4/lib/events/events_internal.h +++ b/source4/lib/events/events_internal.h @@ -47,6 +47,12 @@ struct event_ops { struct iocb *iocb, event_aio_handler_t handler, void *private_data); + /* signal functions */ + struct signal_event *(*add_signal)(struct event_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + event_signal_handler_t handler, + void *private_data); /* loop functions */ int (*loop_once)(struct event_context *ev); @@ -77,6 +83,14 @@ struct timed_event { void *additional_data; }; +struct signal_event { + struct signal_event *prev, *next; + struct event_context *event_ctx; + event_signal_handler_t handler; + void *private_data; + int signum; +}; + /* aio event is private to the aio backend */ struct aio_event; @@ -89,6 +103,12 @@ struct event_context { /* this is private for the events_ops implementation */ void *additional_data; + + /* number of signal event handlers */ + int num_signal_handlers; + + /* pipe hack used with signal handlers */ + struct fd_event *pipe_fde; }; @@ -98,3 +118,11 @@ struct timed_event *common_event_add_timed(struct event_context *, TALLOC_CTX *, struct timeval, event_timed_handler_t, void *); void common_event_loop_timer(struct event_context *); struct timeval common_event_loop_delay(struct event_context *); + +struct signal_event *common_event_add_signal(struct event_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + event_signal_handler_t handler, + void *private_data); +int common_event_check_signal(struct event_context *ev); + diff --git a/source4/lib/events/events_liboop.c b/source4/lib/events/events_liboop.c index 53238662f0..96bdafd956 100644 --- a/source4/lib/events/events_liboop.c +++ b/source4/lib/events/events_liboop.c @@ -274,6 +274,7 @@ static const struct event_ops event_oop_ops = { .get_fd_flags = oop_event_get_fd_flags, .set_fd_flags = oop_event_set_fd_flags, .add_timed = oop_event_add_timed, + .add_signal = common_event_add_signal, .loop_once = oop_event_loop_once, .loop_wait = oop_event_loop_wait, }; diff --git a/source4/lib/events/events_select.c b/source4/lib/events/events_select.c index 5f4ecba5ca..ea84353183 100644 --- a/source4/lib/events/events_select.c +++ b/source4/lib/events/events_select.c @@ -197,8 +197,19 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru } } + if (select_ev->ev->num_signal_handlers && + common_event_check_signal(select_ev->ev)) { + return 0; + } + selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + if (selrtn == -1 && errno == EINTR && + select_ev->ev->num_signal_handlers) { + common_event_check_signal(select_ev->ev); + return 0; + } + if (selrtn == -1 && errno == EBADF) { /* the socket is dead! this should never happen as the socket should have first been @@ -279,6 +290,7 @@ static const struct event_ops select_event_ops = { .get_fd_flags = select_event_get_fd_flags, .set_fd_flags = select_event_set_fd_flags, .add_timed = common_event_add_timed, + .add_signal = common_event_add_signal, .loop_once = select_event_loop_once, .loop_wait = select_event_loop_wait, }; diff --git a/source4/lib/events/events_signal.c b/source4/lib/events/events_signal.c new file mode 100644 index 0000000000..85b42ae4ab --- /dev/null +++ b/source4/lib/events/events_signal.c @@ -0,0 +1,153 @@ +/* + Unix SMB/CIFS implementation. + + common events code for signal events + + Copyright (C) Andrew Tridgell 2007 + + 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. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/select.h" +#include "lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "lib/events/events_internal.h" + +#define NUM_SIGNALS 64 + +/* + the poor design of signals means that this table must be static global +*/ +static struct { + struct signal_event *sig_handlers[NUM_SIGNALS]; + uint32_t signal_count[NUM_SIGNALS]; + uint32_t got_signal; + int pipe_hack[2]; +} sig_state; + + +/* + signal handler - redirects to registered signals +*/ +static void signal_handler(int signum) +{ + char c = 0; + sig_state.signal_count[signum]++; + sig_state.got_signal++; + /* doesn't matter if this pipe overflows */ + write(sig_state.pipe_hack[1], &c, 1); +} + + +/* + destroy a signal event +*/ +static int signal_event_destructor(struct signal_event *se) +{ + se->event_ctx->num_signal_handlers--; + DLIST_REMOVE(sig_state.sig_handlers[se->signum], se); + if (sig_state.sig_handlers[se->signum] == NULL) { + signal(se->signum, SIG_DFL); + } + return 0; +} + +/* + this is part of the pipe hack needed to avoid the signal race condition +*/ +static void signal_pipe_handler(struct event_context *ev, struct fd_event *fde, + uint16_t flags, void *private) +{ + char c[16]; + /* its non-blocking, doesn't matter if we read too much */ + read(sig_state.pipe_hack[0], c, sizeof(c)); +} + +/* + add a signal event + return NULL on failure (memory allocation error) +*/ +struct signal_event *common_event_add_signal(struct event_context *ev, + TALLOC_CTX *mem_ctx, + int signum, + event_signal_handler_t handler, + void *private_data) +{ + struct signal_event *se; + + if (signum >= NUM_SIGNALS) { + return NULL; + } + + se = talloc(mem_ctx?mem_ctx:ev, struct signal_event); + if (se == NULL) return NULL; + + se->event_ctx = ev; + se->handler = handler; + se->private_data = private_data; + se->signum = signum; + + if (sig_state.sig_handlers[signum] == NULL) { + signal(signum, signal_handler); + } + + DLIST_ADD(sig_state.sig_handlers[signum], se); + + talloc_set_destructor(se, signal_event_destructor); + + if (ev->pipe_fde == NULL) { + if (sig_state.pipe_hack[0] == 0 && + sig_state.pipe_hack[1] == 0) { + pipe(sig_state.pipe_hack); + set_blocking(sig_state.pipe_hack[0], False); + set_blocking(sig_state.pipe_hack[1], False); + } + ev->pipe_fde = event_add_fd(ev, ev, sig_state.pipe_hack[0], + EVENT_FD_READ, signal_pipe_handler, NULL); + } + ev->num_signal_handlers++; + + return se; +} + + +/* + check if a signal is pending + return != 0 if a signal was pending +*/ +int common_event_check_signal(struct event_context *ev) +{ + int i; + if (sig_state.got_signal == 0) { + return 0; + } + + for (i=0;inext; + se->handler(ev, se, i, count, se->private_data); + } + sig_state.signal_count[i] -= count; + sig_state.got_signal -= count; + } + } + + return 1; +} diff --git a/source4/lib/events/events_standard.c b/source4/lib/events/events_standard.c index b0f5259fa7..9c470235d0 100644 --- a/source4/lib/events/events_standard.c +++ b/source4/lib/events/events_standard.c @@ -229,8 +229,19 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv timeout = ((tvalp->tv_usec+999) / 1000) + (tvalp->tv_sec*1000); } + if (epoll_ev->ev->num_signal_handlers && + common_event_check_signal(epoll_ev->ev)) { + return 0; + } + ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout); + if (ret == -1 && errno == EINTR && epoll_ev->ev->num_signal_handlers) { + if (common_event_check_signal(epoll_ev->ev)) { + return 0; + } + } + if (ret == -1 && errno != EINTR) { epoll_fallback_to_select(std_ev, "epoll_wait() failed"); return -1; @@ -434,8 +445,19 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva } } + if (std_ev->ev->num_signal_handlers && + common_event_check_signal(std_ev->ev)) { + return 0; + } + selrtn = select(std_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + if (selrtn == -1 && errno == EINTR && + std_ev->ev->num_signal_handlers) { + common_event_check_signal(std_ev->ev); + return 0; + } + if (selrtn == -1 && errno == EBADF) { /* the socket is dead! this should never happen as the socket should have first been @@ -520,6 +542,7 @@ static const struct event_ops std_event_ops = { .get_fd_flags = std_event_get_fd_flags, .set_fd_flags = std_event_set_fd_flags, .add_timed = common_event_add_timed, + .add_signal = common_event_add_signal, .loop_once = std_event_loop_once, .loop_wait = std_event_loop_wait, }; -- cgit