summaryrefslogtreecommitdiffstats
path: root/ctdb
diff options
context:
space:
mode:
authorAmitay Isaacs <amitay@gmail.com>2012-04-13 17:18:53 +1000
committerAmitay Isaacs <amitay@gmail.com>2012-04-13 17:28:15 +1000
commit195cf3c87e3c6c9f1784aa483110a64bc8760703 (patch)
tree848393dcef13341e6f6f50f6d7d1eafbd228cc12 /ctdb
parentf16a180f6f952227adbde8189d81c969dceee065 (diff)
downloadsamba-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.dox2
-rw-r--r--ctdb/lib/tevent/libtevent.m42
-rw-r--r--ctdb/lib/tevent/testsuite.c4
-rw-r--r--ctdb/lib/tevent/tevent.c23
-rw-r--r--ctdb/lib/tevent/tevent.h357
-rw-r--r--ctdb/lib/tevent/tevent_epoll.c23
-rw-r--r--ctdb/lib/tevent/tevent_fd.c6
-rw-r--r--ctdb/lib/tevent/tevent_internal.h9
-rw-r--r--ctdb/lib/tevent/tevent_poll.c312
-rw-r--r--ctdb/lib/tevent/tevent_queue.c99
-rw-r--r--ctdb/lib/tevent/tevent_req.c19
-rw-r--r--ctdb/lib/tevent/tevent_select.c11
-rw-r--r--ctdb/lib/tevent/tevent_signal.c42
-rw-r--r--ctdb/lib/tevent/tevent_standard.c24
-rw-r--r--ctdb/lib/tevent/tevent_timed.c4
-rw-r--r--ctdb/lib/tevent/tevent_util.c17
-rw-r--r--ctdb/lib/tevent/tevent_util.h2
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. */