diff options
| author | Amitay Isaacs <amitay@gmail.com> | 2012-04-13 17:18:53 +1000 |
|---|---|---|
| committer | Amitay Isaacs <amitay@gmail.com> | 2012-04-13 17:28:15 +1000 |
| commit | 195cf3c87e3c6c9f1784aa483110a64bc8760703 (patch) | |
| tree | 848393dcef13341e6f6f50f6d7d1eafbd228cc12 /ctdb | |
| parent | f16a180f6f952227adbde8189d81c969dceee065 (diff) | |
| download | samba-195cf3c87e3c6c9f1784aa483110a64bc8760703.tar.gz samba-195cf3c87e3c6c9f1784aa483110a64bc8760703.tar.xz samba-195cf3c87e3c6c9f1784aa483110a64bc8760703.zip | |
lib/tevent: Sync tevent from samba git tree
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
(This used to be ctdb commit 483459c79884891b3639a8bb865d5e8318cde98c)
Diffstat (limited to 'ctdb')
| -rw-r--r-- | ctdb/lib/tevent/ABI/tevent-0.9.15.sigs (renamed from ctdb/lib/tevent/ABI/tevent-0.9.9.sigs) | 5 | ||||
| -rw-r--r-- | ctdb/lib/tevent/doc/mainpage.dox | 2 | ||||
| -rw-r--r-- | ctdb/lib/tevent/libtevent.m4 | 2 | ||||
| -rw-r--r-- | ctdb/lib/tevent/testsuite.c | 4 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent.c | 23 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent.h | 357 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_epoll.c | 23 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_fd.c | 6 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_internal.h | 9 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_poll.c | 312 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_queue.c | 99 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_req.c | 19 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_select.c | 11 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_signal.c | 42 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_standard.c | 24 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_timed.c | 4 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_util.c | 17 | ||||
| -rw-r--r-- | ctdb/lib/tevent/tevent_util.h | 2 |
18 files changed, 885 insertions, 76 deletions
diff --git a/ctdb/lib/tevent/ABI/tevent-0.9.9.sigs b/ctdb/lib/tevent/ABI/tevent-0.9.15.sigs index 9adaba579b..13c461cc56 100644 --- a/ctdb/lib/tevent/ABI/tevent-0.9.9.sigs +++ b/ctdb/lib/tevent/ABI/tevent-0.9.15.sigs @@ -14,6 +14,7 @@ _tevent_req_done: void (struct tevent_req *, const char *) _tevent_req_error: bool (struct tevent_req *, uint64_t, const char *) _tevent_req_nomem: bool (const void *, struct tevent_req *, const char *) _tevent_req_notify_callback: void (struct tevent_req *, const char *) +_tevent_req_oom: void (struct tevent_req *, const char *) _tevent_schedule_immediate: void (struct tevent_immediate *, struct tevent_context *, tevent_immediate_handler_t, void *, const char *, const char *) tevent_backend_list: const char **(TALLOC_CTX *) tevent_cleanup_pending_signal_handlers: void (struct tevent_signal *) @@ -40,12 +41,16 @@ tevent_fd_set_flags: void (struct tevent_fd *, uint16_t) tevent_loop_allow_nesting: void (struct tevent_context *) tevent_loop_set_nesting_hook: void (struct tevent_context *, tevent_nesting_hook, void *) tevent_queue_add: bool (struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *) +tevent_queue_add_entry: struct tevent_queue_entry *(struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *) +tevent_queue_add_optimize_empty: struct tevent_queue_entry *(struct tevent_queue *, struct tevent_context *, struct tevent_req *, tevent_queue_trigger_fn_t, void *) tevent_queue_length: size_t (struct tevent_queue *) +tevent_queue_running: bool (struct tevent_queue *) tevent_queue_start: void (struct tevent_queue *) tevent_queue_stop: void (struct tevent_queue *) tevent_re_initialise: int (struct tevent_context *) tevent_register_backend: bool (const char *, const struct tevent_ops *) tevent_req_default_print: char *(struct tevent_req *, TALLOC_CTX *) +tevent_req_defer_callback: void (struct tevent_req *, struct tevent_context *) tevent_req_is_error: bool (struct tevent_req *, enum tevent_req_state *, uint64_t *) tevent_req_is_in_progress: bool (struct tevent_req *) tevent_req_poll: bool (struct tevent_req *, struct tevent_context *) diff --git a/ctdb/lib/tevent/doc/mainpage.dox b/ctdb/lib/tevent/doc/mainpage.dox index 5cd3969c78..e2f986e1ef 100644 --- a/ctdb/lib/tevent/doc/mainpage.dox +++ b/ctdb/lib/tevent/doc/mainpage.dox @@ -8,7 +8,7 @@ * signals, and the classic file descriptor events. * * Tevent also provide helpers to deal with asynchronous code providing the - * tevent_req (tevent tequest) functions. + * tevent_req (tevent request) functions. * * @section tevent_download Download * diff --git a/ctdb/lib/tevent/libtevent.m4 b/ctdb/lib/tevent/libtevent.m4 index c7e2c5a2df..409232a866 100644 --- a/ctdb/lib/tevent/libtevent.m4 +++ b/ctdb/lib/tevent/libtevent.m4 @@ -39,7 +39,7 @@ if test x"$INCLUDED_TEVENT" != x"no" ; then TEVENT_OBJ="tevent.o tevent_debug.o tevent_util.o" TEVENT_OBJ="$TEVENT_OBJ tevent_fd.o tevent_timed.o tevent_immediate.o tevent_signal.o" TEVENT_OBJ="$TEVENT_OBJ tevent_req.o tevent_wakeup.o tevent_queue.o" - TEVENT_OBJ="$TEVENT_OBJ tevent_standard.o tevent_select.o" + TEVENT_OBJ="$TEVENT_OBJ tevent_standard.o tevent_select.o tevent_poll.o" AC_SUBST(TEVENT_OBJ) TEVENT_CFLAGS="-I$teventdir" diff --git a/ctdb/lib/tevent/testsuite.c b/ctdb/lib/tevent/testsuite.c index 3a763de6db..b674689669 100644 --- a/ctdb/lib/tevent/testsuite.c +++ b/ctdb/lib/tevent/testsuite.c @@ -101,7 +101,9 @@ static bool test_event_context(struct torture_context *test, #ifdef SA_RESTART se1 = event_add_signal(ev_ctx, ev_ctx, SIGALRM, SA_RESTART, count_handler, &alarm_count); #endif +#ifdef SA_RESETHAND se2 = event_add_signal(ev_ctx, ev_ctx, SIGALRM, SA_RESETHAND, count_handler, &alarm_count); +#endif #ifdef SA_SIGINFO se3 = event_add_signal(ev_ctx, ev_ctx, SIGUSR1, SA_SIGINFO, count_handler, &info_count); #endif @@ -146,7 +148,7 @@ static bool test_event_context(struct torture_context *test, struct torture_suite *torture_local_event(TALLOC_CTX *mem_ctx) { - struct torture_suite *suite = torture_suite_create(mem_ctx, "EVENT"); + struct torture_suite *suite = torture_suite_create(mem_ctx, "event"); const char **list = event_backend_list(suite); int i; diff --git a/ctdb/lib/tevent/tevent.c b/ctdb/lib/tevent/tevent.c index 5eec5ccf56..d3421a2718 100644 --- a/ctdb/lib/tevent/tevent.c +++ b/ctdb/lib/tevent/tevent.c @@ -64,9 +64,6 @@ #include "tevent_internal.h" #include "tevent_util.h" -/* needed for the special ctdbd "track if time jumps unexpectedly */ -#include <time.h> - struct tevent_ops_list { struct tevent_ops_list *next, *prev; const char *name; @@ -91,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; @@ -107,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); } /* @@ -117,6 +113,7 @@ void tevent_set_default_backend(const char *backend) static void tevent_backend_init(void) { tevent_select_init(); + tevent_poll_init(); tevent_standard_init(); #ifdef HAVE_EPOLL tevent_epoll_init(); @@ -188,6 +185,17 @@ int tevent_common_context_destructor(struct tevent_context *ev) 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; } @@ -394,7 +402,6 @@ struct tevent_immediate *_tevent_create_immediate(TALLOC_CTX *mem_ctx, /* schedule an immediate event - return NULL on failure */ void _tevent_schedule_immediate(struct tevent_immediate *im, struct tevent_context *ev, @@ -581,14 +588,12 @@ done: 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 */ diff --git a/ctdb/lib/tevent/tevent.h b/ctdb/lib/tevent/tevent.h index 2a826153eb..b76e3a3769 100644 --- a/ctdb/lib/tevent/tevent.h +++ b/ctdb/lib/tevent/tevent.h @@ -111,7 +111,7 @@ typedef void (*tevent_signal_handler_t)(struct tevent_context *ev, struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx); /** - * @brief Create a event_context structure and name it. + * @brief Create a event_context structure and select a specific backend. * * 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 @@ -119,7 +119,7 @@ struct tevent_context *tevent_context_init(TALLOC_CTX *mem_ctx); * * @param[in] mem_ctx The memory context to use. * - * @param[in] name The name for the tevent context. + * @param[in] name The name of the backend to use. * * @return An allocated tevent context, NULL on error. */ @@ -136,7 +136,7 @@ struct tevent_context *tevent_context_init_byname(TALLOC_CTX *mem_ctx, const cha const char **tevent_backend_list(TALLOC_CTX *mem_ctx); /** - * @brief Set the default tevent backent. + * @brief Set the default tevent backend. * * @param[in] backend The name of the backend to set. */ @@ -509,50 +509,121 @@ int tevent_set_debug_stderr(struct tevent_context *ev); * @defgroup tevent_request The tevent request functions. * @ingroup tevent * - * This represents an async request being processed by callbacks via an event - * context. A user can issue for example a write request to a socket, giving - * an implementation function the fd, the buffer and the number of bytes to - * transfer. The function issuing the request will immediately return without - * blocking most likely without having sent anything. The API user then fills - * in req->async.fn and req->async.private_data, functions that are called - * when the request is finished. + * A tevent_req represents an asynchronous computation. * - * It is up to the user of the async request to talloc_free it after it has - * finished. This can happen while the completion function is called. + * The tevent_req group of API calls is the recommended way of + * programming async computations within tevent. In particular the + * file descriptor (tevent_add_fd) and timer (tevent_add_timed) events + * are considered too low-level to be used in larger computations. To + * read and write from and to sockets, Samba provides two calls on top + * of tevent_add_fd: read_packet_send/recv and writev_send/recv. These + * requests are much easier to compose than the low-level event + * handlers called from tevent_add_fd. + * + * A lot of the simplicity tevent_req has brought to the notoriously + * hairy async programming came via a set of conventions that every + * async computation programmed should follow. One central piece of + * these conventions is the naming of routines and variables. + * + * Every async computation needs a name (sensibly called "computation" + * down from here). From this name quite a few naming conventions are + * derived. + * + * Every computation that requires local state needs a + * @code + * struct computation_state { + * int local_var; + * }; + * @endcode + * Even if no local variables are required, such a state struct should + * be created containing a dummy variable. Quite a few helper + * functions and macros (for example tevent_req_create()) assume such + * a state struct. + * + * An async computation is started by a computation_send + * function. When it is finished, its result can be received by a + * computation_recv function. For an example how to set up an async + * computation, see the code example in the documentation for + * tevent_req_create() and tevent_req_post(). The prototypes for _send + * and _recv functions should follow some conventions: + * + * @code + * struct tevent_req *computation_send(TALLOC_CTX *mem_ctx, + * struct tevent_req *ev, + * ... further args); + * int computation_recv(struct tevent_req *req, ... further output args); + * @endcode + * + * The "int" result of computation_recv() depends on the result the + * sync version of the function would have, "int" is just an example + * here. + * + * Another important piece of the conventions is that the program flow + * is interrupted as little as possible. Because a blocking + * sub-computation requires that the flow needs to continue in a + * separate function that is the logical sequel of some computation, + * it should lexically follow sending off the blocking + * sub-computation. Setting the callback function via + * tevent_req_set_callback() requires referencing a function lexically + * below the call to tevent_req_set_callback(), forward declarations + * are required. A lot of the async computations thus begin with a + * sequence of declarations such as + * + * @code + * static void computation_step1_done(struct tevent_req *subreq); + * static void computation_step2_done(struct tevent_req *subreq); + * static void computation_step3_done(struct tevent_req *subreq); + * @endcode + * + * It really helps readability a lot to do these forward declarations, + * because the lexically sequential program flow makes the async + * computations almost as clear to read as a normal, sync program + * flow. + * + * It is up to the user of the async computation to talloc_free it + * after it has finished. If an async computation should be aborted, + * the tevent_req structure can be talloc_free'ed. After it has + * finished, it should talloc_free'ed by the API user. * * @{ */ /** - * An async request moves between the following 4 states: + * An async request moves from TEVENT_REQ_INIT to + * TEVENT_REQ_IN_PROGRESS. All other states are valid after a request + * has finished. */ enum tevent_req_state { /** - * we are creating the request + * We are creating the request */ TEVENT_REQ_INIT, /** - * we are waiting the request to complete + * We are waiting the request to complete */ TEVENT_REQ_IN_PROGRESS, /** - * the request is finished + * The request is finished successfully */ TEVENT_REQ_DONE, /** - * A user error has occurred + * A user error has occurred. The user error has been + * indicated by tevent_req_error(), it can be retrieved via + * tevent_req_is_error(). */ TEVENT_REQ_USER_ERROR, /** - * Request timed out + * Request timed out after the timeout set by tevent_req_set_endtime. */ TEVENT_REQ_TIMED_OUT, /** - * No memory in between + * An internal allocation has failed, or tevent_req_nomem has + * been given a NULL pointer as the first argument. */ TEVENT_REQ_NO_MEMORY, /** - * the request is already received by the caller + * The request has been received by the caller. No further + * action is valid. */ TEVENT_REQ_RECEIVED }; @@ -572,6 +643,9 @@ typedef void (*tevent_req_fn)(struct tevent_req *req); /** * @brief Set an async request callback. * + * See the documentation of tevent_req_post() for an example how this + * is supposed to be used. + * * @param[in] req The async request to set the callback. * * @param[in] fn The callback function to set. @@ -583,9 +657,17 @@ void tevent_req_set_callback(struct tevent_req *req, tevent_req_fn fn, void *pvt #ifdef DOXYGEN /** - * @brief Get the private data casted to the given type for a callback from + * @brief Get the private data cast to the given type for a callback from * a tevent request structure. * + * @code + * static void computation_done(struct tevent_req *subreq) { + * struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + * struct computation_state *state = tevent_req_data(req, struct computation_state); + * .... more things, eventually maybe call tevent_req_done(req); + * } + * @endcode + * * @param[in] req The structure to get the callback data from. * * @param[in] type The type of the private callback data to get. @@ -619,11 +701,17 @@ void *tevent_req_callback_data_void(struct tevent_req *req); /** * @brief Get the private data from a tevent request structure. * + * When the tevent_req has been created by tevent_req_create, the + * result of tevent_req_data() is the state variable created by + * tevent_req_create() as a child of the req. + * * @param[in] req The structure to get the private data from. * + * @param[in] type The type of the private data + * * @return The private data or NULL if not set. */ -void *tevent_req_data(struct tevent_req *req); +void *tevent_req_data(struct tevent_req *req, #type); #else void *_tevent_req_data(struct tevent_req *req); #define tevent_req_data(_req, _type) \ @@ -755,22 +843,29 @@ bool _tevent_req_cancel(struct tevent_req *req, const char *location); /** * @brief Create an async tevent request. * - * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS. + * The new async request will be initialized in state TEVENT_REQ_IN_PROGRESS. * - * @param[in] mem_ctx The memory context for the result. - * - * @param[in] pstate The private state of the request. + * @code + * struct tevent_req *req; + * struct computation_state *state; + * req = tevent_req_create(mem_ctx, &state, struct computation_state); + * @endcode * - * @param[in] state_size The size of the private state of the request. + * Tevent_req_create() creates the state variable as a talloc child of + * its result. The state variable should be used as the talloc parent + * for all temporary variables that are allocated during the async + * computation. This way, when the user of the async computation frees + * the request, the state as a talloc child will be free'd along with + * all the temporary variables hanging off the state. * + * @param[in] mem_ctx The memory context for the result. + * @param[in] pstate Pointer to the private request state. * @param[in] type The name of the request. * * @return A new async request. NULL on error. */ struct tevent_req *tevent_req_create(TALLOC_CTX *mem_ctx, - void *pstate, - size_t state_size, - const char *type); + void **pstate, #type); #else struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, void *pstate, @@ -900,19 +995,52 @@ bool _tevent_req_nomem(const void *p, _tevent_req_nomem(p, req, __location__) #endif +#ifdef DOXYGEN +/** + * @brief Indicate out of memory to a request + * + * @param[in] req The request being processed. + */ +void tevent_req_oom(struct tevent_req *req); +#else +void _tevent_req_oom(struct tevent_req *req, + const char *location); +#define tevent_req_oom(req) \ + _tevent_req_oom(req, __location__) +#endif + /** * @brief Finish a request before the caller had the change to set the callback. * * An implementation of an async request might find that it can either finish - * the request without waiting for an external event, or it can't even start + * the request without waiting for an external event, or it can not even start * the engine. To present the illusion of a callback to the user of the API, * the implementation can call this helper function which triggers an - * immediate timed event. This way the caller can use the same calling + * immediate event. This way the caller can use the same calling * conventions, independent of whether the request was actually deferred. * + * @code + * struct tevent_req *computation_send(TALLOC_CTX *mem_ctx, + * struct tevent_context *ev) + * { + * struct tevent_req *req, *subreq; + * struct computation_state *state; + * req = tevent_req_create(mem_ctx, &state, struct computation_state); + * if (req == NULL) { + * return NULL; + * } + * subreq = subcomputation_send(state, ev); + * if (tevent_req_nomem(subreq, req)) { + * return tevent_req_post(req, ev); + * } + * tevent_req_set_callback(subreq, computation_done, req); + * return req; + * } + * @endcode + * * @param[in] req The finished request. * - * @param[in] ev The tevent_context for the timed event. + * @param[in] ev The tevent_context for the immediate event. * * @return The given request will be returned. */ @@ -920,12 +1048,52 @@ struct tevent_req *tevent_req_post(struct tevent_req *req, struct tevent_context *ev); /** + * @brief Finish multiple requests within one function + * + * Normally tevent_req_notify_callback() and all wrappers + * (e.g. tevent_req_done() and tevent_req_error()) + * need to be the last thing an event handler should call. + * This is because the callback is likely to destroy the + * context of the current function. + * + * If a function wants to notify more than one caller, + * it is dangerous if it just triggers multiple callbacks + * in a row. With tevent_req_defer_callback() it is possible + * to set an event context that will be used to defer the callback + * via an immediate event (similar to tevent_req_post()). + * + * @code + * struct complete_state { + * struct tevent_context *ev; + * + * struct tevent_req **reqs; + * }; + * + * void complete(struct complete_state *state) + * { + * size_t i, c = talloc_array_length(state->reqs); + * + * for (i=0; i < c; i++) { + * tevent_req_defer_callback(state->reqs[i], state->ev); + * tevent_req_done(state->reqs[i]); + * } + * } + * @endcode + * + * @param[in] req The finished request. + * + * @param[in] ev The tevent_context for the immediate event. + * + * @return The given request will be returned. + */ +void tevent_req_defer_callback(struct tevent_req *req, + struct tevent_context *ev); + +/** * @brief Check if the given request is still in progress. * * It is typically used by sync wrapper functions. * - * This function destroys the attached private data. - * * @param[in] req The request to poll. * * @return The boolean form of "is in progress". @@ -963,7 +1131,21 @@ bool tevent_req_poll(struct tevent_req *req, struct tevent_context *ev); /** - * @brief Get the tevent request and the actual error code you've set. + * @brief Get the tevent request state and the actual error set by + * tevent_req_error. + * + * @code + * int computation_recv(struct tevent_req *req, uint64_t *perr) + * { + * enum tevent_req_state state; + * uint64_t err; + * if (tevent_req_is_error(req, &state, &err)) { + * *perr = err; + * return -1; + * } + * return 0; + * } + * @endcode * * @param[in] req The tevent request to get the error from. * @@ -1004,7 +1186,7 @@ void tevent_req_received(struct tevent_req *req); * * Example: * @code - * static my_callback_wakeup_done(tevent_req *req) + * static void my_callback_wakeup_done(tevent_req *subreq) * { * struct tevent_req *req = tevent_req_callback_data(subreq, * struct tevent_req); @@ -1092,7 +1274,7 @@ struct timeval tevent_timeval_current(void); * * @param[in] secs The seconds to set. * - * @param[in] usecs The milliseconds to set. + * @param[in] usecs The microseconds to set. * * @return A timeval structure with the given values. */ @@ -1127,7 +1309,7 @@ bool tevent_timeval_is_zero(const struct timeval *tv); * * @param[in] secs The seconds to add to the timeval. * - * @param[in] usecs The milliseconds to add to the timeval. + * @param[in] usecs The microseconds to add to the timeval. * * @return The timeval structure with the new time. */ @@ -1139,7 +1321,7 @@ struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs, * * @param[in] secs The seconds of the offset from now. * - * @param[in] usecs The milliseconds of the offset from now. + * @param[in] usecs The microseconds of the offset from now. * * @return A timval with the given offset in the future. */ @@ -1165,6 +1347,7 @@ struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs); */ struct tevent_queue; +struct tevent_queue_entry; #ifdef DOXYGEN /** @@ -1176,8 +1359,8 @@ struct tevent_queue; * * @return An allocated tevent queue on success, NULL on error. * - * @see tevent_start() - * @see tevent_stop() + * @see tevent_queue_start() + * @see tevent_queue_stop() */ struct tevent_queue *tevent_queue_create(TALLOC_CTX *mem_ctx, const char *name); @@ -1199,6 +1382,8 @@ struct tevent_queue *_tevent_queue_create(TALLOC_CTX *mem_ctx, * tevent_queue_add(). * * @see tevent_queue_add() + * @see tevent_queue_add_entry() + * @see tevent_queue_add_optimize_empty() */ typedef void (*tevent_queue_trigger_fn_t)(struct tevent_req *req, void *private_data); @@ -1213,7 +1398,9 @@ typedef void (*tevent_queue_trigger_fn_t)(struct tevent_req *req, * @param[in] req The tevent request to add to the queue. * * @param[in] trigger The function triggered by the queue when the request - * is called. + * is called. Since tevent 0.9.14 it's possible to + * pass NULL, in order to just add a "blocker" to the + * queue. * * @param[in] private_data The private data passed to the trigger function. * @@ -1227,6 +1414,79 @@ bool tevent_queue_add(struct tevent_queue *queue, void *private_data); /** + * @brief Add a tevent request to the queue. + * + * The request can be removed from the queue by calling talloc_free() + * (or a similar function) on the returned queue entry. This + * is the only difference to tevent_queue_add(). + * + * @param[in] queue The queue to add the request. + * + * @param[in] ev The event handle to use for the request. + * + * @param[in] req The tevent request to add to the queue. + * + * @param[in] trigger The function triggered by the queue when the request + * is called. Since tevent 0.9.14 it's possible to + * pass NULL, in order to just add a "blocker" to the + * queue. + * + * @param[in] private_data The private data passed to the trigger function. + * + * @return a pointer to the tevent_queue_entry if the request + * has been successfully added, NULL otherwise. + * + * @see tevent_queue_add() + * @see tevent_queue_add_optimize_empty() + */ +struct tevent_queue_entry *tevent_queue_add_entry( + struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data); + +/** + * @brief Add a tevent request to the queue using a possible optimization. + * + * This tries to optimize for the empty queue case and may calls + * the trigger function directly. This is the only difference compared + * to tevent_queue_add_entry(). + * + * The caller needs to be prepared that the trigger function has + * already called tevent_req_notify_callback(), tevent_req_error(), + * tevent_req_done() or a similar function. + * + * The request can be removed from the queue by calling talloc_free() + * (or a similar function) on the returned queue entry. + * + * @param[in] queue The queue to add the request. + * + * @param[in] ev The event handle to use for the request. + * + * @param[in] req The tevent request to add to the queue. + * + * @param[in] trigger The function triggered by the queue when the request + * is called. Since tevent 0.9.14 it's possible to + * pass NULL, in order to just add a "blocker" to the + * queue. + * + * @param[in] private_data The private data passed to the trigger function. + * + * @return a pointer to the tevent_queue_entry if the request + * has been successfully added, NULL otherwise. + * + * @see tevent_queue_add() + * @see tevent_queue_add_entry() + */ +struct tevent_queue_entry *tevent_queue_add_optimize_empty( + struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data); + +/** * @brief Start a tevent queue. * * The queue is started by default. @@ -1253,6 +1513,17 @@ void tevent_queue_stop(struct tevent_queue *queue); */ size_t tevent_queue_length(struct tevent_queue *queue); +/** + * @brief Is the tevent queue running. + * + * The queue is started by default. + * + * @param[in] queue The queue. + * + * @return Wether the queue is running or not.. + */ +bool tevent_queue_running(struct tevent_queue *queue); + typedef int (*tevent_nesting_hook)(struct tevent_context *ev, void *private_data, uint32_t level, diff --git a/ctdb/lib/tevent/tevent_epoll.c b/ctdb/lib/tevent/tevent_epoll.c index a475f38235..f5a69ddc50 100644 --- a/ctdb/lib/tevent/tevent_epoll.c +++ b/ctdb/lib/tevent/tevent_epoll.c @@ -42,8 +42,7 @@ struct epoll_event_context { }; /* - called when a epoll call fails, and we should fallback - to using select + called when a epoll call fails */ static void epoll_panic(struct epoll_event_context *epoll_ev, const char *reason) { @@ -79,11 +78,20 @@ static int epoll_ctx_destructor(struct epoll_event_context *epoll_ev) static int epoll_init_ctx(struct epoll_event_context *epoll_ev) { epoll_ev->epoll_fd = epoll_create(64); - epoll_ev->pid = getpid(); - talloc_set_destructor(epoll_ev, epoll_ctx_destructor); if (epoll_ev->epoll_fd == -1) { + tevent_debug(epoll_ev->ev, TEVENT_DEBUG_FATAL, + "Failed to create epoll handle.\n"); return -1; } + + if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) { + tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING, + "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); + } + + epoll_ev->pid = getpid(); + talloc_set_destructor(epoll_ev, epoll_ctx_destructor); + return 0; } @@ -109,6 +117,12 @@ static void epoll_check_reopen(struct epoll_event_context *epoll_ev) "Failed to recreate epoll handle after fork\n"); return; } + + if (!ev_set_close_on_exec(epoll_ev->epoll_fd)) { + tevent_debug(epoll_ev->ev, TEVENT_DEBUG_WARNING, + "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); + } + epoll_ev->pid = getpid(); for (fde=epoll_ev->ev->fd_events;fde;fde=fde->next) { epoll_add_event(epoll_ev, fde); @@ -251,6 +265,7 @@ static int epoll_event_loop(struct epoll_event_context *epoll_ev, struct timeval } ret = epoll_wait(epoll_ev->epoll_fd, events, MAXEVENTS, timeout); + if (ret == -1 && errno == EINTR && epoll_ev->ev->signal_events) { if (tevent_common_check_signal(epoll_ev->ev)) { return 0; diff --git a/ctdb/lib/tevent/tevent_fd.c b/ctdb/lib/tevent/tevent_fd.c index c58e8e1ab1..455961b67c 100644 --- a/ctdb/lib/tevent/tevent_fd.c +++ b/ctdb/lib/tevent/tevent_fd.c @@ -51,6 +51,12 @@ struct tevent_fd *tevent_common_add_fd(struct tevent_context *ev, TALLOC_CTX *me { struct tevent_fd *fde; + /* tevent will crash later on select() if we save + * a negative file descriptor. Better to fail here + * so that consumers will be able to debug it + */ + if (fd < 0) return NULL; + fde = talloc(mem_ctx?mem_ctx:ev, struct tevent_fd); if (!fde) return NULL; diff --git a/ctdb/lib/tevent/tevent_internal.h b/ctdb/lib/tevent/tevent_internal.h index ba03bc50bc..7f1d8766ca 100644 --- a/ctdb/lib/tevent/tevent_internal.h +++ b/ctdb/lib/tevent/tevent_internal.h @@ -142,6 +142,12 @@ struct tevent_req { struct tevent_immediate *trigger; /** + * @brief An event context which will be used to + * defer the _tevent_req_notify_callback(). + */ + struct tevent_context *defer_callback_ev; + + /** * @brief the timer event if tevent_req_set_endtime was used * */ @@ -162,7 +168,7 @@ struct tevent_fd { const char *handler_name; const char *location; /* this is private for the events_ops implementation */ - uint16_t additional_flags; + uint64_t additional_flags; void *additional_data; }; @@ -303,6 +309,7 @@ void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se); bool tevent_standard_init(void); bool tevent_select_init(void); +bool tevent_poll_init(void); #ifdef HAVE_EPOLL bool tevent_epoll_init(void); #endif diff --git a/ctdb/lib/tevent/tevent_poll.c b/ctdb/lib/tevent/tevent_poll.c new file mode 100644 index 0000000000..d2e45c88be --- /dev/null +++ b/ctdb/lib/tevent/tevent_poll.c @@ -0,0 +1,312 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) Stefan Metzmacher 2005-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 + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" +#include "system/select.h" +#include "tevent.h" +#include "tevent_util.h" +#include "tevent_internal.h" + +struct poll_event_context { + /* + * These two arrays are maintained together. + */ + struct pollfd *fds; + struct tevent_fd **fd_events; + uint64_t num_fds; + + /* information for exiting from the event loop */ + int exit_code; +}; + +/* + create a select_event_context structure. +*/ +static int poll_event_context_init(struct tevent_context *ev) +{ + struct poll_event_context *poll_ev; + + poll_ev = talloc_zero(ev, struct poll_event_context); + if (poll_ev == NULL) { + return -1; + } + ev->additional_data = poll_ev; + return 0; +} + +/* + destroy an fd_event +*/ +static int poll_event_fd_destructor(struct tevent_fd *fde) +{ + struct tevent_context *ev = fde->event_ctx; + struct poll_event_context *poll_ev = NULL; + struct tevent_fd *moved_fde; + uint64_t del_idx = fde->additional_flags; + + if (ev == NULL) { + goto done; + } + + poll_ev = talloc_get_type_abort( + ev->additional_data, struct poll_event_context); + + moved_fde = poll_ev->fd_events[poll_ev->num_fds-1]; + poll_ev->fd_events[del_idx] = moved_fde; + poll_ev->fds[del_idx] = poll_ev->fds[poll_ev->num_fds-1]; + moved_fde->additional_flags = del_idx; + + poll_ev->num_fds -= 1; +done: + return tevent_common_fd_destructor(fde); +} + +/* + add a fd based event + return NULL on failure (memory allocation error) +*/ +static struct tevent_fd *poll_event_add_fd(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + int fd, uint16_t flags, + tevent_fd_handler_t handler, + void *private_data, + const char *handler_name, + const char *location) +{ + struct poll_event_context *poll_ev = talloc_get_type_abort( + ev->additional_data, struct poll_event_context); + struct pollfd *pfd; + struct tevent_fd *fde; + + fde = tevent_common_add_fd(ev, mem_ctx, fd, flags, + handler, private_data, + handler_name, location); + if (fde == NULL) { + return NULL; + } + + /* we allocate 16 slots to avoid a lot of reallocations */ + if (talloc_array_length(poll_ev->fds) == poll_ev->num_fds) { + struct pollfd *tmp_fds; + struct tevent_fd **tmp_fd_events; + tmp_fds = talloc_realloc( + poll_ev, poll_ev->fds, struct pollfd, + poll_ev->num_fds + 16); + if (tmp_fds == NULL) { + TALLOC_FREE(fde); + return NULL; + } + poll_ev->fds = tmp_fds; + + tmp_fd_events = talloc_realloc( + poll_ev, poll_ev->fd_events, struct tevent_fd *, + poll_ev->num_fds + 16); + if (tmp_fd_events == NULL) { + TALLOC_FREE(fde); + return NULL; + } + poll_ev->fd_events = tmp_fd_events; + } + + pfd = &poll_ev->fds[poll_ev->num_fds]; + + pfd->fd = fd; + + pfd->events = 0; + pfd->revents = 0; + + if (flags & TEVENT_FD_READ) { + pfd->events |= (POLLIN|POLLHUP); + } + if (flags & TEVENT_FD_WRITE) { + pfd->events |= (POLLOUT); + } + + fde->additional_flags = poll_ev->num_fds; + poll_ev->fd_events[poll_ev->num_fds] = fde; + + poll_ev->num_fds += 1; + + talloc_set_destructor(fde, poll_event_fd_destructor); + + return fde; +} + +/* + set the fd event flags +*/ +static void poll_event_set_fd_flags(struct tevent_fd *fde, uint16_t flags) +{ + struct poll_event_context *poll_ev = talloc_get_type_abort( + fde->event_ctx->additional_data, struct poll_event_context); + uint64_t idx = fde->additional_flags; + uint16_t pollflags = 0; + + if (flags & TEVENT_FD_READ) { + pollflags |= (POLLIN|POLLHUP); + } + if (flags & TEVENT_FD_WRITE) { + pollflags |= (POLLOUT); + } + + poll_ev->fds[idx].events = pollflags; + + fde->flags = flags; +} + +/* + event loop handling using poll() +*/ +static int poll_event_loop_poll(struct tevent_context *ev, + struct timeval *tvalp) +{ + struct poll_event_context *poll_ev = talloc_get_type_abort( + ev->additional_data, struct poll_event_context); + struct tevent_fd *fde; + int pollrtn; + int timeout = -1; + + if (ev->signal_events && tevent_common_check_signal(ev)) { + return 0; + } + + if (tvalp != NULL) { + timeout = tvalp->tv_sec * 1000; + timeout += (tvalp->tv_usec + 999) / 1000; + } + + pollrtn = poll(poll_ev->fds, poll_ev->num_fds, timeout); + + if (pollrtn == -1 && errno == EINTR && ev->signal_events) { + tevent_common_check_signal(ev); + return 0; + } + + if (pollrtn == -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. */ + tevent_debug(ev, TEVENT_DEBUG_FATAL, + "ERROR: EBADF on poll_event_loop_once\n"); + poll_ev->exit_code = EBADF; + return -1; + } + + if (pollrtn == 0 && tvalp) { + /* we don't care about a possible delay here */ + tevent_common_loop_timer_delay(ev); + return 0; + } + + if (pollrtn <= 0) { + /* + * No fd's ready + */ + return 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 (fde = ev->fd_events; fde; fde = fde->next) { + struct pollfd *pfd; + uint64_t pfd_idx = fde->additional_flags; + uint16_t flags = 0; + + pfd = &poll_ev->fds[pfd_idx]; + + if (pfd->revents & (POLLHUP|POLLERR)) { + /* If we only wait for TEVENT_FD_WRITE, we + should not tell the event handler about it, + and remove the writable flag, as we only + report errors when waiting for read events + to match the select behavior. */ + if (!(fde->flags & TEVENT_FD_READ)) { + TEVENT_FD_NOT_WRITEABLE(fde); + continue; + } + flags |= TEVENT_FD_READ; + } + if (pfd->revents & POLLIN) { + flags |= TEVENT_FD_READ; + } + if (pfd->revents & POLLOUT) { + flags |= TEVENT_FD_WRITE; + } + if (flags != 0) { + fde->handler(ev, fde, flags, fde->private_data); + break; + } + } + + return 0; +} + +/* + do a single event loop using the events defined in ev +*/ +static int poll_event_loop_once(struct tevent_context *ev, + const char *location) +{ + struct timeval tval; + + if (ev->signal_events && + tevent_common_check_signal(ev)) { + return 0; + } + + if (ev->immediate_events && + tevent_common_loop_immediate(ev)) { + return 0; + } + + tval = tevent_common_loop_timer_delay(ev); + if (tevent_timeval_is_zero(&tval)) { + return 0; + } + + return poll_event_loop_poll(ev, &tval); +} + +static const struct tevent_ops poll_event_ops = { + .context_init = poll_event_context_init, + .add_fd = poll_event_add_fd, + .set_fd_close_fn = tevent_common_fd_set_close_fn, + .get_fd_flags = tevent_common_fd_get_flags, + .set_fd_flags = poll_event_set_fd_flags, + .add_timer = tevent_common_add_timer, + .schedule_immediate = tevent_common_schedule_immediate, + .add_signal = tevent_common_add_signal, + .loop_once = poll_event_loop_once, + .loop_wait = tevent_common_loop_wait, +}; + +_PRIVATE_ bool tevent_poll_init(void) +{ + return tevent_register_backend("poll", &poll_event_ops); +} diff --git a/ctdb/lib/tevent/tevent_queue.c b/ctdb/lib/tevent/tevent_queue.c index 3715c35e4f..4750675802 100644 --- a/ctdb/lib/tevent/tevent_queue.c +++ b/ctdb/lib/tevent/tevent_queue.c @@ -144,17 +144,19 @@ static void tevent_queue_immediate_trigger(struct tevent_context *ev, q->list->trigger(q->list->req, q->list->private_data); } -bool tevent_queue_add(struct tevent_queue *queue, - struct tevent_context *ev, - struct tevent_req *req, - tevent_queue_trigger_fn_t trigger, - void *private_data) +static struct tevent_queue_entry *tevent_queue_add_internal( + struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data, + bool allow_direct) { struct tevent_queue_entry *e; e = talloc_zero(req, struct tevent_queue_entry); if (e == NULL) { - return false; + return NULL; } e->queue = queue; @@ -163,16 +165,53 @@ bool tevent_queue_add(struct tevent_queue *queue, e->trigger = trigger; e->private_data = private_data; + /* + * if there is no trigger, it is just a blocker + */ + if (trigger == NULL) { + e->triggered = true; + } + + if (queue->length > 0) { + /* + * if there are already entries in the + * queue do not optimize. + */ + allow_direct = false; + } + + if (req->async.fn != NULL) { + /* + * If the callers wants to optimize for the + * empty queue case, call the trigger only + * if there is no callback defined for the + * request yet. + */ + allow_direct = false; + } + DLIST_ADD_END(queue->list, e, struct tevent_queue_entry *); queue->length++; talloc_set_destructor(e, tevent_queue_entry_destructor); if (!queue->running) { - return true; + return e; } if (queue->list->triggered) { - return true; + return e; + } + + /* + * If allowed we directly call the trigger + * avoiding possible delays caused by + * an immediate event. + */ + if (allow_direct) { + queue->list->triggered = true; + queue->list->trigger(queue->list->req, + queue->list->private_data); + return e; } tevent_schedule_immediate(queue->immediate, @@ -180,9 +219,48 @@ bool tevent_queue_add(struct tevent_queue *queue, tevent_queue_immediate_trigger, queue); + return e; +} + +bool tevent_queue_add(struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +{ + struct tevent_queue_entry *e; + + e = tevent_queue_add_internal(queue, ev, req, + trigger, private_data, false); + if (e == NULL) { + return false; + } + return true; } +struct tevent_queue_entry *tevent_queue_add_entry( + struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +{ + return tevent_queue_add_internal(queue, ev, req, + trigger, private_data, false); +} + +struct tevent_queue_entry *tevent_queue_add_optimize_empty( + struct tevent_queue *queue, + struct tevent_context *ev, + struct tevent_req *req, + tevent_queue_trigger_fn_t trigger, + void *private_data) +{ + return tevent_queue_add_internal(queue, ev, req, + trigger, private_data, true); +} + void tevent_queue_start(struct tevent_queue *queue) { if (queue->running) { @@ -215,3 +293,8 @@ size_t tevent_queue_length(struct tevent_queue *queue) { return queue->length; } + +bool tevent_queue_running(struct tevent_queue *queue) +{ + return queue->running; +} diff --git a/ctdb/lib/tevent/tevent_req.c b/ctdb/lib/tevent/tevent_req.c index b0c9c57dde..d8d0c5f564 100644 --- a/ctdb/lib/tevent/tevent_req.c +++ b/ctdb/lib/tevent/tevent_req.c @@ -74,6 +74,7 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, talloc_free(req); return NULL; } + req->internal.defer_callback_ev = NULL; data = talloc_zero_size(req, data_size); if (data == NULL) { @@ -91,6 +92,11 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, void _tevent_req_notify_callback(struct tevent_req *req, const char *location) { req->internal.finish_location = location; + if (req->internal.defer_callback_ev) { + (void)tevent_req_post(req, req->internal.defer_callback_ev); + req->internal.defer_callback_ev = NULL; + return; + } if (req->async.fn != NULL) { req->async.fn(req); } @@ -123,6 +129,11 @@ bool _tevent_req_error(struct tevent_req *req, return true; } +void _tevent_req_oom(struct tevent_req *req, const char *location) +{ + tevent_req_finish(req, TEVENT_REQ_NO_MEMORY, location); +} + bool _tevent_req_nomem(const void *p, struct tevent_req *req, const char *location) @@ -130,7 +141,7 @@ bool _tevent_req_nomem(const void *p, if (p != NULL) { return false; } - tevent_req_finish(req, TEVENT_REQ_NO_MEMORY, location); + _tevent_req_oom(req, location); return true; } @@ -164,6 +175,12 @@ struct tevent_req *tevent_req_post(struct tevent_req *req, return req; } +void tevent_req_defer_callback(struct tevent_req *req, + struct tevent_context *ev) +{ + req->internal.defer_callback_ev = ev; +} + bool tevent_req_is_in_progress(struct tevent_req *req) { if (req->internal.state == TEVENT_REQ_IN_PROGRESS) { diff --git a/ctdb/lib/tevent/tevent_select.c b/ctdb/lib/tevent/tevent_select.c index dc525b2bb2..51c1dec4a6 100644 --- a/ctdb/lib/tevent/tevent_select.c +++ b/ctdb/lib/tevent/tevent_select.c @@ -111,6 +111,11 @@ static struct tevent_fd *select_event_add_fd(struct tevent_context *ev, TALLOC_C struct select_event_context); struct tevent_fd *fde; + if (fd < 0 || fd >= FD_SETSIZE) { + errno = EBADF; + return NULL; + } + fde = tevent_common_add_fd(ev, mem_ctx, fd, flags, handler, private_data, handler_name, location); @@ -144,6 +149,11 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru /* setup any fd events */ for (fde = select_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->fd < 0 || fde->fd >= FD_SETSIZE) { + errno = EBADF; + return -1; + } + if (fde->flags & TEVENT_FD_READ) { FD_SET(fde->fd, &r_fds); } @@ -158,6 +168,7 @@ static int select_event_loop_select(struct select_event_context *select_ev, stru } selrtn = select(select_ev->maxfd+1, &r_fds, &w_fds, NULL, tvalp); + if (selrtn == -1 && errno == EINTR && select_ev->ev->signal_events) { tevent_common_check_signal(select_ev->ev); diff --git a/ctdb/lib/tevent/tevent_signal.c b/ctdb/lib/tevent/tevent_signal.c index c505419a2d..56bcef9150 100644 --- a/ctdb/lib/tevent/tevent_signal.c +++ b/ctdb/lib/tevent/tevent_signal.c @@ -208,7 +208,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, /* the sig_state needs to be on a global context as it can last across multiple event contexts */ if (sig_state == NULL) { - sig_state = talloc_zero(talloc_autofree_context(), struct tevent_sig_state); + sig_state = talloc_zero(NULL, struct tevent_sig_state); if (sig_state == NULL) { return NULL; } @@ -307,6 +307,15 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev, return se; } +struct tevent_se_exists { + struct tevent_se_exists **myself; +}; + +static int tevent_se_exists_destructor(struct tevent_se_exists *s) +{ + *s->myself = NULL; + return 0; +} /* check if a signal is pending @@ -335,7 +344,25 @@ int tevent_common_check_signal(struct tevent_context *ev) } for (sl=sig_state->sig_handlers[i];sl;sl=next) { struct tevent_signal *se = sl->se; + struct tevent_se_exists *exists; + next = sl->next; + + /* + * We have to be careful to not touch "se" + * after it was deleted in its handler. Thus + * we allocate a child whose destructor will + * tell by nulling out itself that its parent + * is gone. + */ + exists = talloc(se, struct tevent_se_exists); + if (exists == NULL) { + continue; + } + exists->myself = &exists; + talloc_set_destructor( + exists, tevent_se_exists_destructor); + #ifdef SA_SIGINFO if (se->sa_flags & SA_SIGINFO) { uint32_t j; @@ -352,17 +379,26 @@ int tevent_common_check_signal(struct tevent_context *ev) se->handler(ev, se, i, 1, (void*)&sig_state->sig_info[i][ofs], se->private_data); + if (!exists) { + break; + } } - if (se->sa_flags & SA_RESETHAND) { +#ifdef SA_RESETHAND + if (exists && (se->sa_flags & SA_RESETHAND)) { talloc_free(se); } +#endif + talloc_free(exists); continue; } #endif se->handler(ev, se, i, count, NULL, se->private_data); - if (se->sa_flags & SA_RESETHAND) { +#ifdef SA_RESETHAND + if (exists && (se->sa_flags & SA_RESETHAND)) { talloc_free(se); } +#endif + talloc_free(exists); } #ifdef SA_SIGINFO diff --git a/ctdb/lib/tevent/tevent_standard.c b/ctdb/lib/tevent/tevent_standard.c index e79e602efa..534576c108 100644 --- a/ctdb/lib/tevent/tevent_standard.c +++ b/ctdb/lib/tevent/tevent_standard.c @@ -100,6 +100,17 @@ static int epoll_ctx_destructor(struct std_event_context *std_ev) static void epoll_init_ctx(struct std_event_context *std_ev) { std_ev->epoll_fd = epoll_create(64); + if (std_ev->epoll_fd == -1) { + tevent_debug(std_ev->ev, TEVENT_DEBUG_FATAL, + "Failed to create epoll handle.\n"); + return; + } + + if (!ev_set_close_on_exec(std_ev->epoll_fd)) { + tevent_debug(std_ev->ev, TEVENT_DEBUG_WARNING, + "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); + } + std_ev->pid = getpid(); talloc_set_destructor(std_ev, epoll_ctx_destructor); } @@ -126,6 +137,12 @@ static void epoll_check_reopen(struct std_event_context *std_ev) "Failed to recreate epoll handle after fork\n"); return; } + + if (!ev_set_close_on_exec(std_ev->epoll_fd)) { + tevent_debug(std_ev->ev, TEVENT_DEBUG_WARNING, + "Failed to set close-on-exec, file descriptor may be leaked to children.\n"); + } + std_ev->pid = getpid(); for (fde=std_ev->ev->fd_events;fde;fde=fde->next) { epoll_add_event(std_ev, fde); @@ -262,6 +279,7 @@ static int epoll_event_loop(struct std_event_context *std_ev, struct timeval *tv } ret = epoll_wait(std_ev->epoll_fd, events, MAXEVENTS, timeout); + if (ret == -1 && errno == EINTR && std_ev->ev->signal_events) { if (tevent_common_check_signal(std_ev->ev)) { return 0; @@ -456,6 +474,10 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva /* setup any fd events */ for (fde = std_ev->ev->fd_events; fde; fde = fde->next) { + if (fde->fd < 0 || fde->fd >= FD_SETSIZE) { + std_ev->exit_code = EBADF; + return -1; + } if (fde->flags & TEVENT_FD_READ) { FD_SET(fde->fd, &r_fds); } @@ -504,7 +526,7 @@ static int std_event_loop_select(struct std_event_context *std_ev, struct timeva if (FD_ISSET(fde->fd, &r_fds)) flags |= TEVENT_FD_READ; if (FD_ISSET(fde->fd, &w_fds)) flags |= TEVENT_FD_WRITE; - if (flags) { + if (flags & fde->flags) { fde->handler(std_ev->ev, fde, flags, fde->private_data); break; } diff --git a/ctdb/lib/tevent/tevent_timed.c b/ctdb/lib/tevent/tevent_timed.c index 457ef1c21b..cc25428f18 100644 --- a/ctdb/lib/tevent/tevent_timed.c +++ b/ctdb/lib/tevent/tevent_timed.c @@ -197,7 +197,7 @@ struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_C /* do a single event loop using the events defined in ev - return the delay untill the next timed event, + return the delay until the next timed event, or zero if a timed event was triggered */ struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev) @@ -208,7 +208,7 @@ struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev) if (!te) { /* have a default tick time of 30 seconds. This guarantees that code that uses its own timeout checking will be - able to proceeed eventually */ + able to proceed eventually */ return tevent_timeval_set(30, 0); } diff --git a/ctdb/lib/tevent/tevent_util.c b/ctdb/lib/tevent/tevent_util.c index f78cd8f707..16af8f3b90 100644 --- a/ctdb/lib/tevent/tevent_util.c +++ b/ctdb/lib/tevent/tevent_util.c @@ -88,3 +88,20 @@ int ev_set_blocking(int fd, bool set) return fcntl( fd, F_SETFL, val); #undef FLAG_TO_SET } + +bool ev_set_close_on_exec(int fd) +{ +#ifdef FD_CLOEXEC + int val; + + val = fcntl(fd, F_GETFD, 0); + if (val >= 0) { + val |= FD_CLOEXEC; + val = fcntl(fd, F_SETFD, val); + if (val != -1) { + return true; + } + } +#endif + return false; +} diff --git a/ctdb/lib/tevent/tevent_util.h b/ctdb/lib/tevent/tevent_util.h index 2f4bc2f043..311be604a0 100644 --- a/ctdb/lib/tevent/tevent_util.h +++ b/ctdb/lib/tevent/tevent_util.h @@ -183,7 +183,7 @@ do { \ const char **ev_str_list_add(const char **list, const char *s); int ev_set_blocking(int fd, bool set); size_t ev_str_list_length(const char **list); - +bool ev_set_close_on_exec(int fd); /* Defined here so we can build against older talloc versions that don't * have this define yet. */ |
