tevent: add tevent_context_wrapper_create() infrastructure
authorStefan Metzmacher <metze@samba.org>
Tue, 22 Jul 2014 14:51:38 +0000 (16:51 +0200)
committerRalph Boehme <slow@samba.org>
Wed, 11 Jul 2018 21:04:21 +0000 (23:04 +0200)
This allows to specify wrapper tevent_contexts, which adds the ability
to run functions before and after the event handler functions.

This can be used to implement impersonation hooks
or advanced debugging/profiling hooks.

We'll undo the 0.9.36 ABI change on the 0.9.37 release
at the end of this patchset.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
14 files changed:
lib/tevent/ABI/tevent-0.9.36.sigs
lib/tevent/tevent.c
lib/tevent/tevent.h
lib/tevent/tevent_debug.c
lib/tevent/tevent_epoll.c
lib/tevent/tevent_fd.c
lib/tevent/tevent_immediate.c
lib/tevent/tevent_internal.h
lib/tevent/tevent_poll.c
lib/tevent/tevent_signal.c
lib/tevent/tevent_threads.c
lib/tevent/tevent_timed.c
lib/tevent/tevent_wrapper.c [new file with mode: 0644]
lib/tevent/wscript

index 443bb7cb6c92642d3c7fa0d238d959ab5225149e..ddb2c03b65c20f7ecf3873508d418888ec9722b0 100644 (file)
@@ -1,6 +1,9 @@
 _tevent_add_fd: struct tevent_fd *(struct tevent_context *, TALLOC_CTX *, int, uint16_t, tevent_fd_handler_t, void *, const char *, const char *)
 _tevent_add_signal: struct tevent_signal *(struct tevent_context *, TALLOC_CTX *, int, int, tevent_signal_handler_t, void *, const char *, const char *)
 _tevent_add_timer: struct tevent_timer *(struct tevent_context *, TALLOC_CTX *, struct timeval, tevent_timer_handler_t, void *, const char *, const char *)
+_tevent_context_pop_use: void (struct tevent_context *, const char *)
+_tevent_context_push_use: bool (struct tevent_context *, const char *)
+_tevent_context_wrapper_create: struct tevent_context *(struct tevent_context *, TALLOC_CTX *, const struct tevent_wrapper_ops *, void *, size_t, const char *, const char *)
 _tevent_create_immediate: struct tevent_immediate *(TALLOC_CTX *, const char *)
 _tevent_loop_once: int (struct tevent_context *, const char *)
 _tevent_loop_until: int (struct tevent_context *, bool (*)(void *), void *, const char *)
@@ -47,6 +50,8 @@ tevent_common_wakeup_init: int (struct tevent_context *)
 tevent_context_init: struct tevent_context *(TALLOC_CTX *)
 tevent_context_init_byname: struct tevent_context *(TALLOC_CTX *, const char *)
 tevent_context_init_ops: struct tevent_context *(TALLOC_CTX *, const struct tevent_ops *, void *)
+tevent_context_is_wrapper: bool (struct tevent_context *)
+tevent_context_same_loop: bool (struct tevent_context *, struct tevent_context *)
 tevent_debug: void (struct tevent_context *, enum tevent_debug_level, const char *, ...)
 tevent_fd_get_flags: uint16_t (struct tevent_fd *)
 tevent_fd_set_auto_close: void (struct tevent_fd *)
index de0436df37366da4ad1b60224b3b22ea31ca963b..dbec1821e41c7297e1e8bd1c85126e1fd59d8112 100644 (file)
@@ -298,10 +298,17 @@ int tevent_common_context_destructor(struct tevent_context *ev)
        struct tevent_timer *te, *tn;
        struct tevent_immediate *ie, *in;
        struct tevent_signal *se, *sn;
-
+       struct tevent_wrapper_glue *gl, *gn;
 #ifdef HAVE_PTHREAD
        int ret;
+#endif
+
+       if (ev->wrapper.glue != NULL) {
+               tevent_abort(ev,
+                       "tevent_common_context_destructor() active on wrapper");
+       }
 
+#ifdef HAVE_PTHREAD
        ret = pthread_mutex_lock(&tevent_contexts_mutex);
        if (ret != 0) {
                abort();
@@ -345,10 +352,18 @@ int tevent_common_context_destructor(struct tevent_context *ev)
        }
 #endif
 
+       for (gl = ev->wrapper.list; gl; gl = gn) {
+               gn = gl->next;
+
+               gl->main_ev = NULL;
+               DLIST_REMOVE(ev->wrapper.list, gl);
+       }
+
        tevent_common_wakeup_fini(ev);
 
        for (fd = ev->fd_events; fd; fd = fn) {
                fn = fd->next;
+               fd->wrapper = NULL;
                fd->event_ctx = NULL;
                DLIST_REMOVE(ev->fd_events, fd);
        }
@@ -356,12 +371,14 @@ int tevent_common_context_destructor(struct tevent_context *ev)
        ev->last_zero_timer = NULL;
        for (te = ev->timer_events; te; te = tn) {
                tn = te->next;
+               te->wrapper = NULL;
                te->event_ctx = NULL;
                DLIST_REMOVE(ev->timer_events, te);
        }
 
        for (ie = ev->immediate_events; ie; ie = in) {
                in = ie->next;
+               ie->wrapper = NULL;
                ie->event_ctx = NULL;
                ie->cancel_fn = NULL;
                DLIST_REMOVE(ev->immediate_events, ie);
@@ -369,6 +386,7 @@ int tevent_common_context_destructor(struct tevent_context *ev)
 
        for (se = ev->signal_events; se; se = sn) {
                sn = se->next;
+               se->wrapper = NULL;
                se->event_ctx = NULL;
                DLIST_REMOVE(ev->signal_events, se);
                /*
@@ -675,6 +693,16 @@ struct tevent_signal *_tevent_add_signal(struct tevent_context *ev,
 
 void tevent_loop_allow_nesting(struct tevent_context *ev)
 {
+       if (ev->wrapper.glue != NULL) {
+               tevent_abort(ev, "tevent_loop_allow_nesting() on wrapper");
+               return;
+       }
+
+       if (ev->wrapper.list != NULL) {
+               tevent_abort(ev, "tevent_loop_allow_nesting() with wrapper");
+               return;
+       }
+
        ev->nesting.allowed = true;
 }
 
index 0e2e806ee5c450baed9eef6904ec86c67ce522fe..d34a03093f36f1f8256518f4fd2d704ab11820ca 100644 (file)
@@ -429,6 +429,12 @@ int _tevent_loop_wait(struct tevent_context *ev, const char *location);
  *
  * @param[in] fde       File descriptor event on which to set the destructor
  * @param[in] close_fn  Destructor to execute when fde is freed
+ *
+ * @note That the close_fn() on tevent_fd is *NOT* wrapped on contexts
+ * created by tevent_context_wrapper_create()!
+ *
+ * @see tevent_fd_set_close_fn
+ * @see tevent_context_wrapper_create
  */
 void tevent_fd_set_close_fn(struct tevent_fd *fde,
                            tevent_fd_close_fn_t close_fn);
@@ -439,6 +445,8 @@ void tevent_fd_set_close_fn(struct tevent_fd *fde,
  * This function calls close(fd) internally.
  *
  * @param[in] fde  File descriptor event to auto-close
+ *
+ * @see tevent_fd_set_close_fn
  */
 void tevent_fd_set_auto_close(struct tevent_fd *fde);
 
@@ -1967,6 +1975,258 @@ bool tevent_register_backend(const char *name, const struct tevent_ops *ops);
 
 /* @} */
 
+/**
+ * @defgroup tevent_wrapper_ops The tevent wrapper operation functions
+ * @ingroup tevent
+ *
+ * The following structure and registration functions are exclusively
+ * needed for people writing wrapper functions for event handlers
+ * e.g. wrappers can be used for debugging/profiling or impersonation.
+ *
+ * There is nothing useful for normal tevent user in here.
+ *
+ * @note That the close_fn() on tevent_fd is *NOT* wrapped!
+ *
+ * @see tevent_context_wrapper_create
+ * @see tevent_fd_set_auto_close
+ * @{
+ */
+
+struct tevent_wrapper_ops {
+       const char *name;
+
+       bool (*before_use)(struct tevent_context *wrap_ev,
+                          void *private_state,
+                          struct tevent_context *main_ev,
+                          const char *location);
+       void (*after_use)(struct tevent_context *wrap_ev,
+                         void *private_state,
+                         struct tevent_context *main_ev,
+                         const char *location);
+
+       void (*before_fd_handler)(struct tevent_context *wrap_ev,
+                                 void *private_state,
+                                 struct tevent_context *main_ev,
+                                 struct tevent_fd *fde,
+                                 uint16_t flags,
+                                 const char *handler_name,
+                                 const char *location);
+       void (*after_fd_handler)(struct tevent_context *wrap_ev,
+                                void *private_state,
+                                struct tevent_context *main_ev,
+                                struct tevent_fd *fde,
+                                uint16_t flags,
+                                const char *handler_name,
+                                const char *location);
+
+       void (*before_timer_handler)(struct tevent_context *wrap_ev,
+                                    void *private_state,
+                                    struct tevent_context *main_ev,
+                                    struct tevent_timer *te,
+                                    struct timeval requested_time,
+                                    struct timeval trigger_time,
+                                    const char *handler_name,
+                                    const char *location);
+       void (*after_timer_handler)(struct tevent_context *wrap_ev,
+                                   void *private_state,
+                                   struct tevent_context *main_ev,
+                                   struct tevent_timer *te,
+                                   struct timeval requested_time,
+                                   struct timeval trigger_time,
+                                   const char *handler_name,
+                                   const char *location);
+
+       void (*before_immediate_handler)(struct tevent_context *wrap_ev,
+                                        void *private_state,
+                                        struct tevent_context *main_ev,
+                                        struct tevent_immediate *im,
+                                        const char *handler_name,
+                                        const char *location);
+       void (*after_immediate_handler)(struct tevent_context *wrap_ev,
+                                       void *private_state,
+                                       struct tevent_context *main_ev,
+                                       struct tevent_immediate *im,
+                                       const char *handler_name,
+                                       const char *location);
+
+       void (*before_signal_handler)(struct tevent_context *wrap_ev,
+                                     void *private_state,
+                                     struct tevent_context *main_ev,
+                                     struct tevent_signal *se,
+                                     int signum,
+                                     int count,
+                                     void *siginfo,
+                                     const char *handler_name,
+                                     const char *location);
+       void (*after_signal_handler)(struct tevent_context *wrap_ev,
+                                    void *private_state,
+                                    struct tevent_context *main_ev,
+                                    struct tevent_signal *se,
+                                    int signum,
+                                    int count,
+                                    void *siginfo,
+                                    const char *handler_name,
+                                    const char *location);
+};
+
+#ifdef DOXYGEN
+/**
+ * @brief Create a wrapper tevent_context.
+ *
+ * @param[in]  main_ev        The main event context to work on.
+ *
+ * @param[in]  mem_ctx        The talloc memory context to use.
+ *
+ * @param[in]  ops            The tevent_wrapper_ops function table.
+ *
+ * @param[out] private_state  The private state use by the wrapper functions.
+ *
+ * @param[in]  private_type   The talloc type of the private_state.
+ *
+ * @return                    The wrapper event context, NULL on error.
+ *
+ * @note Available as of tevent 0.9.37
+ */
+struct tevent_context *tevent_context_wrapper_create(struct tevent_context *main_ev,
+                                               TALLOC_CTX *mem_ctx,
+                                               const struct tevent_wrapper_ops *ops,
+                                               void **private_state,
+                                               const char *private_type);
+#else
+struct tevent_context *_tevent_context_wrapper_create(struct tevent_context *main_ev,
+                                               TALLOC_CTX *mem_ctx,
+                                               const struct tevent_wrapper_ops *ops,
+                                               void *pstate,
+                                               size_t psize,
+                                               const char *type,
+                                               const char *location);
+#define tevent_context_wrapper_create(main_ev, mem_ctx, ops, state, type) \
+       _tevent_context_wrapper_create(main_ev, mem_ctx, ops, \
+                                      state, sizeof(type), #type, __location__)
+#endif
+
+/**
+ * @brief Check if the event context is a wrapper event context.
+ *
+ * @param[in]  ev       The event context to work on.
+ *
+ * @return              Is a wrapper (true), otherwise (false).
+ *
+ * @see tevent_context_wrapper_create()
+ *
+ * @note Available as of tevent 0.9.37
+ */
+bool tevent_context_is_wrapper(struct tevent_context *ev);
+
+#ifdef DOXYGEN
+/**
+ * @brief Prepare the environment of a (wrapper) event context.
+ *
+ * A caller might call this before passing a wrapper event context
+ * to a tevent_req based *_send() function.
+ *
+ * The wrapper event context might do something like impersonation.
+ *
+ * tevent_context_push_use() must always be used in combination
+ * with tevent_context_pop_use().
+ *
+ * There is a global stack of currently active/busy wrapper event contexts.
+ * Each wrapper can only appear once on that global stack!
+ * The stack size is limited to 32 elements, which should be enough
+ * for all useful scenarios.
+ *
+ * In addition to an explicit tevent_context_push_use() also
+ * the invocation of an immediate, timer or fd handler implicitly
+ * pushes the wrapper on the stack.
+ *
+ * Therefore there are some strict constraints for the usage of
+ * tevent_context_push_use():
+ * - It must not be called from within an event handler
+ *   that already acts on the wrapper.
+ * - tevent_context_pop_use() must be called before
+ *   leaving the code block that called tevent_context_push_use().
+ * - The caller is responsible ensure the correct stack ordering
+ * - Any violation of these constraints results in calling
+ *   the abort handler of the given tevent context.
+ *
+ * Calling tevent_context_push_use() on a raw event context
+ * still consumes an element on the stack, but it's otherwise
+ * a no-op.
+ *
+ * If tevent_context_push_use() returns false, it means
+ * that the wrapper's before_use() hook returned this failure,
+ * in that case you must not call tevent_context_pop_use() as
+ * the wrapper is not pushed onto the stack.
+ *
+ * @param[in]  ev       The event context to work on.
+ *
+ * @return              Success (true) or failure (false).
+ *
+ * @note This is only needed if wrapper event contexts are in use.
+ *
+ * @see tevent_context_pop_use
+ *
+ * @note Available as of tevent 0.9.37
+ */
+bool tevent_context_push_use(struct tevent_context *ev);
+#else
+bool _tevent_context_push_use(struct tevent_context *ev,
+                               const char *location);
+#define tevent_context_push_use(ev) \
+       _tevent_context_push_use(ev, __location__)
+#endif
+
+#ifdef DOXYGEN
+/**
+ * @brief Release the environment of a (wrapper) event context.
+ *
+ * The wrapper event context might undo something like impersonation.
+ *
+ * This must be called after a succesful tevent_context_push_use().
+ * Any ordering violation results in calling
+ * the abort handler of the given tevent context.
+ *
+ * This basically calls the wrapper's after_use() hook.
+ *
+ * @param[in]  ev       The event context to work on.
+ *
+ * @note This is only needed if wrapper event contexts are in use.
+ *
+ * @see tevent_context_push_use
+ *
+ * @note Available as of tevent 0.9.37
+ */
+void tevent_context_pop_use(struct tevent_context *ev);
+#else
+void _tevent_context_pop_use(struct tevent_context *ev,
+                              const char *location);
+#define tevent_context_pop_use(ev) \
+       _tevent_context_pop_use(ev, __location__)
+#endif
+
+/**
+ * @brief Check is the two context pointers belong to the same low level loop
+ *
+ * With the introduction of wrapper contexts it's not trivial
+ * to check if two context pointers belong to the same low level
+ * event loop. Some code may need to know this in order
+ * to make some caching decisions.
+ *
+ * @param[in]  ev1       The first event context.
+ * @param[in]  ev2       The second event context.
+ *
+ * @return true if both contexts belong to the same (still existing) context
+ * loop, false otherwise.
+ *
+ * @see tevent_context_wrapper_create
+ *
+ * @note Available as of tevent 0.9.37
+ */
+bool tevent_context_same_loop(struct tevent_context *ev1,
+                             struct tevent_context *ev2);
+
+/* @} */
+
 /**
  * @defgroup tevent_compat The tevent compatibility functions
  * @ingroup tevent
index 31da7b968366cad509e33bcb8e40caf4455d70ca..0a57639076e28d391718a5fa6e1a1bb10eabc799 100644 (file)
@@ -41,6 +41,13 @@ int tevent_set_debug(struct tevent_context *ev,
                                   va_list ap) PRINTF_ATTRIBUTE(3,0),
                     void *context)
 {
+       if (ev->wrapper.glue != NULL) {
+               ev = tevent_wrapper_main_ev(ev);
+               tevent_abort(ev, "tevent_set_debug() on wrapper");
+               errno = EINVAL;
+               return -1;
+       }
+
        ev->debug_ops.debug = debug;
        ev->debug_ops.context = context;
        return 0;
@@ -86,6 +93,9 @@ void tevent_debug(struct tevent_context *ev, enum tevent_debug_level level,
        if (!ev) {
                return;
        }
+       if (ev->wrapper.glue != NULL) {
+               ev = tevent_wrapper_main_ev(ev);
+       }
        if (ev->debug_ops.debug == NULL) {
                return;
        }
@@ -98,6 +108,12 @@ void tevent_set_trace_callback(struct tevent_context *ev,
                               tevent_trace_callback_t cb,
                               void *private_data)
 {
+       if (ev->wrapper.glue != NULL) {
+               ev = tevent_wrapper_main_ev(ev);
+               tevent_abort(ev, "tevent_set_trace_callback() on wrapper");
+               return;
+       }
+
        ev->tracing.callback = cb;
        ev->tracing.private_data = private_data;
 }
index 5f7ef5d83d173ed31ecf1efa4a18d74d8eda2461..9cbe505c98a4d817f43ec45091ad215add6b239a 100644 (file)
@@ -323,8 +323,10 @@ static int epoll_add_multiplex_fd(struct epoll_event_context *epoll_ev,
                             "add_fde[%p] mpx_fde[%p] fd[%d] - disabling\n",
                             add_fde, mpx_fde, add_fde->fd);
                DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde);
+               mpx_fde->wrapper = NULL;
                mpx_fde->event_ctx = NULL;
                DLIST_REMOVE(epoll_ev->ev->fd_events, add_fde);
+               add_fde->wrapper = NULL;
                add_fde->event_ctx = NULL;
                return 0;
        } else if (ret != 0) {
@@ -387,9 +389,11 @@ static void epoll_add_event(struct epoll_event_context *epoll_ev, struct tevent_
                             "fde[%p] mpx_fde[%p] fd[%d] - disabling\n",
                             fde, mpx_fde, fde->fd);
                DLIST_REMOVE(epoll_ev->ev->fd_events, fde);
+               fde->wrapper = NULL;
                fde->event_ctx = NULL;
                if (mpx_fde != NULL) {
                        DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde);
+                       mpx_fde->wrapper = NULL;
                        mpx_fde->event_ctx = NULL;
                }
                return;
@@ -462,9 +466,11 @@ static void epoll_del_event(struct epoll_event_context *epoll_ev, struct tevent_
                             "fde[%p] mpx_fde[%p] fd[%d] - disabling\n",
                             fde, mpx_fde, fde->fd);
                DLIST_REMOVE(epoll_ev->ev->fd_events, fde);
+               fde->wrapper = NULL;
                fde->event_ctx = NULL;
                if (mpx_fde != NULL) {
                        DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde);
+                       mpx_fde->wrapper = NULL;
                        mpx_fde->event_ctx = NULL;
                }
                return;
@@ -511,9 +517,11 @@ static void epoll_mod_event(struct epoll_event_context *epoll_ev, struct tevent_
                             "fde[%p] mpx_fde[%p] fd[%d] - disabling\n",
                             fde, mpx_fde, fde->fd);
                DLIST_REMOVE(epoll_ev->ev->fd_events, fde);
+               fde->wrapper = NULL;
                fde->event_ctx = NULL;
                if (mpx_fde != NULL) {
                        DLIST_REMOVE(epoll_ev->ev->fd_events, mpx_fde);
+                       mpx_fde->wrapper = NULL;
                        mpx_fde->event_ctx = NULL;
                }
                return;
index 7859cbb00ef51ed47b266c6695b09466f2678c28..b92c45f1dddec1f6b7ec5cc9c0387c9e0e71c0f9 100644 (file)
@@ -51,6 +51,7 @@ done:
        if (fde->busy) {
                return -1;
        }
+       fde->wrapper = NULL;
 
        return 0;
 }
@@ -109,6 +110,8 @@ void tevent_common_fd_set_close_fn(struct tevent_fd *fde,
 int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags,
                                    bool *removed)
 {
+       struct tevent_context *handler_ev = fde->event_ctx;
+
        if (removed != NULL) {
                *removed = false;
        }
@@ -118,7 +121,31 @@ int tevent_common_invoke_fd_handler(struct tevent_fd *fde, uint16_t flags,
        }
 
        fde->busy = true;
-       fde->handler(fde->event_ctx, fde, flags, fde->private_data);
+       if (fde->wrapper != NULL) {
+               handler_ev = fde->wrapper->wrap_ev;
+
+               tevent_wrapper_push_use_internal(handler_ev, fde->wrapper);
+               fde->wrapper->ops->before_fd_handler(
+                                       fde->wrapper->wrap_ev,
+                                       fde->wrapper->private_state,
+                                       fde->wrapper->main_ev,
+                                       fde,
+                                       flags,
+                                       fde->handler_name,
+                                       fde->location);
+       }
+       fde->handler(handler_ev, fde, flags, fde->private_data);
+       if (fde->wrapper != NULL) {
+               fde->wrapper->ops->after_fd_handler(
+                                       fde->wrapper->wrap_ev,
+                                       fde->wrapper->private_state,
+                                       fde->wrapper->main_ev,
+                                       fde,
+                                       flags,
+                                       fde->handler_name,
+                                       fde->location);
+               tevent_wrapper_pop_use_internal(handler_ev, fde->wrapper);
+       }
        fde->busy = false;
 
        if (fde->destroyed) {
index 0649d1eacf2746a361c8e16ba2c5496f6a3c53d2..ef7d8a566c0a6e8e1ccb6a0b08bd4af44cde5c5f 100644 (file)
@@ -100,6 +100,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 {
        const char *create_location = im->create_location;
        bool busy = im->busy;
+       struct tevent_wrapper_glue *glue = im->wrapper;
 
        tevent_common_immediate_cancel(im);
 
@@ -109,6 +110,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 
        *im = (struct tevent_immediate) {
                .event_ctx              = ev,
+               .wrapper                = glue,
                .handler                = handler,
                .private_data           = private_data,
                .handler_name           = handler_name,
@@ -128,6 +130,7 @@ void tevent_common_schedule_immediate(struct tevent_immediate *im,
 int tevent_common_invoke_immediate_handler(struct tevent_immediate *im,
                                           bool *removed)
 {
+       struct tevent_context *handler_ev = im->event_ctx;
        struct tevent_context *ev = im->event_ctx;
        struct tevent_immediate cur = *im;
 
@@ -147,7 +150,29 @@ int tevent_common_invoke_immediate_handler(struct tevent_immediate *im,
        im->busy = true;
        im->handler_name = NULL;
        tevent_common_immediate_cancel(im);
-       cur.handler(ev, im, cur.private_data);
+       if (cur.wrapper != NULL) {
+               handler_ev = cur.wrapper->wrap_ev;
+
+               tevent_wrapper_push_use_internal(handler_ev, cur.wrapper);
+               cur.wrapper->ops->before_immediate_handler(
+                                       cur.wrapper->wrap_ev,
+                                       cur.wrapper->private_state,
+                                       cur.wrapper->main_ev,
+                                       im,
+                                       cur.handler_name,
+                                       cur.schedule_location);
+       }
+       cur.handler(handler_ev, im, cur.private_data);
+       if (cur.wrapper != NULL) {
+               cur.wrapper->ops->after_immediate_handler(
+                                       cur.wrapper->wrap_ev,
+                                       cur.wrapper->private_state,
+                                       cur.wrapper->main_ev,
+                                       im,
+                                       cur.handler_name,
+                                       cur.schedule_location);
+               tevent_wrapper_pop_use_internal(handler_ev, cur.wrapper);
+       }
        im->busy = false;
 
        if (im->destroyed) {
index 1183b9f7f83180ae2f9da64d6d4d04a36adee4ba..17c195816faed72aee85866b76e98f9d0658534b 100644 (file)
@@ -170,6 +170,7 @@ struct tevent_req {
 struct tevent_fd {
        struct tevent_fd *prev, *next;
        struct tevent_context *event_ctx;
+       struct tevent_wrapper_glue *wrapper;
        bool busy;
        bool destroyed;
        int fd;
@@ -189,6 +190,7 @@ struct tevent_fd {
 struct tevent_timer {
        struct tevent_timer *prev, *next;
        struct tevent_context *event_ctx;
+       struct tevent_wrapper_glue *wrapper;
        bool busy;
        bool destroyed;
        struct timeval next_event;
@@ -205,6 +207,7 @@ struct tevent_timer {
 struct tevent_immediate {
        struct tevent_immediate *prev, *next;
        struct tevent_context *event_ctx;
+       struct tevent_wrapper_glue *wrapper;
        bool busy;
        bool destroyed;
        tevent_immediate_handler_t handler;
@@ -222,6 +225,7 @@ struct tevent_immediate {
 struct tevent_signal {
        struct tevent_signal *prev, *next;
        struct tevent_context *event_ctx;
+       struct tevent_wrapper_glue *wrapper;
        bool busy;
        bool destroyed;
        int signum;
@@ -314,6 +318,18 @@ struct tevent_context {
                void *private_data;
        } tracing;
 
+       struct {
+               /*
+                * This is used on the main event context
+                */
+               struct tevent_wrapper_glue *list;
+
+               /*
+                * This is used on the wrapper event context
+                */
+               struct tevent_wrapper_glue *glue;
+       } wrapper;
+
        /*
         * an optimization pointer into timer_events
         * used by used by common code via
@@ -397,6 +413,25 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se,
                                        int signum, int count, void *siginfo,
                                        bool *removed);
 
+struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev);
+
+struct tevent_wrapper_ops;
+
+struct tevent_wrapper_glue {
+       struct tevent_wrapper_glue *prev, *next;
+       struct tevent_context *wrap_ev;
+       struct tevent_context *main_ev;
+       bool busy;
+       bool destroyed;
+       const struct tevent_wrapper_ops *ops;
+       void *private_state;
+};
+
+void tevent_wrapper_push_use_internal(struct tevent_context *ev,
+                                     struct tevent_wrapper_glue *wrapper);
+void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr,
+                                    struct tevent_wrapper_glue *wrapper);
+
 bool tevent_standard_init(void);
 bool tevent_poll_init(void);
 bool tevent_poll_event_add_fd_internal(struct tevent_context *ev,
index 74c418ca42185ce6b7a713e91799893ebed9e3f7..f4c6c2dbe80fe9031d32fa11bf487a6a01b04ee3 100644 (file)
@@ -535,6 +535,7 @@ static int poll_event_loop_poll(struct tevent_context *ev,
                        poll_ev->fdes[idx] = NULL;
                        poll_ev->deleted = true;
                        DLIST_REMOVE(ev->fd_events, fde);
+                       fde->wrapper = NULL;
                        fde->event_ctx = NULL;
                        continue;
                }
@@ -586,6 +587,7 @@ static int poll_event_loop_poll(struct tevent_context *ev,
                        poll_ev->deleted = true;
                        if (fde != NULL) {
                                DLIST_REMOVE(ev->fd_events, fde);
+                               fde->wrapper = NULL;
                                fde->event_ctx = NULL;
                        }
                }
index a787f97b9649384d3f8e5bc6af4c90999b7fc26c..5ca0b8d2ab11c388f72635ac0358c3beb6e3e3eb 100644 (file)
@@ -216,6 +216,7 @@ done:
        if (se->busy) {
                return -1;
        }
+       se->wrapper = NULL;
 
        return 0;
 }
@@ -338,6 +339,7 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se,
                                        int signum, int count, void *siginfo,
                                        bool *removed)
 {
+       struct tevent_context *handler_ev = se->event_ctx;
        bool remove = false;
 
        if (removed != NULL) {
@@ -349,7 +351,35 @@ int tevent_common_invoke_signal_handler(struct tevent_signal *se,
        }
 
        se->busy = true;
-       se->handler(se->event_ctx, se, signum, count, siginfo, se->private_data);
+       if (se->wrapper != NULL) {
+               handler_ev = se->wrapper->wrap_ev;
+
+               tevent_wrapper_push_use_internal(handler_ev, se->wrapper);
+               se->wrapper->ops->before_signal_handler(
+                                               se->wrapper->wrap_ev,
+                                               se->wrapper->private_state,
+                                               se->wrapper->main_ev,
+                                               se,
+                                               signum,
+                                               count,
+                                               siginfo,
+                                               se->handler_name,
+                                               se->location);
+       }
+       se->handler(handler_ev, se, signum, count, siginfo, se->private_data);
+       if (se->wrapper != NULL) {
+               se->wrapper->ops->after_signal_handler(
+                                               se->wrapper->wrap_ev,
+                                               se->wrapper->private_state,
+                                               se->wrapper->main_ev,
+                                               se,
+                                               signum,
+                                               count,
+                                               siginfo,
+                                               se->handler_name,
+                                               se->location);
+               tevent_wrapper_pop_use_internal(handler_ev, se->wrapper);
+       }
        se->busy = false;
 
 #ifdef SA_RESETHAND
index 5d4e0c426769a6f8077d5fbf0ffad2b2f8ef433e..21a9b686ba91a96683905e57620d084e22361678 100644 (file)
@@ -217,6 +217,18 @@ struct tevent_thread_proxy *tevent_thread_proxy_create(
        int pipefds[2];
        struct tevent_thread_proxy *tp;
 
+       if (dest_ev_ctx->wrapper.glue != NULL) {
+               /*
+                * stacking of wrappers is not supported
+                */
+               tevent_debug(dest_ev_ctx->wrapper.glue->main_ev,
+                            TEVENT_DEBUG_FATAL,
+                            "%s() not allowed on a wrapper context\n",
+                            __func__);
+               errno = EINVAL;
+               return NULL;
+       }
+
        tp = talloc_zero(dest_ev_ctx, struct tevent_thread_proxy);
        if (tp == NULL) {
                return NULL;
@@ -375,10 +387,11 @@ void tevent_thread_proxy_schedule(struct tevent_thread_proxy *tp,
 static int tevent_threaded_context_destructor(
        struct tevent_threaded_context *tctx)
 {
+       struct tevent_context *main_ev = tevent_wrapper_main_ev(tctx->event_ctx);
        int ret;
 
-       if (tctx->event_ctx != NULL) {
-               DLIST_REMOVE(tctx->event_ctx->threaded_contexts, tctx);
+       if (main_ev != NULL) {
+               DLIST_REMOVE(main_ev->threaded_contexts, tctx);
        }
 
        /*
@@ -410,10 +423,11 @@ struct tevent_threaded_context *tevent_threaded_context_create(
        TALLOC_CTX *mem_ctx, struct tevent_context *ev)
 {
 #ifdef HAVE_PTHREAD
+       struct tevent_context *main_ev = tevent_wrapper_main_ev(ev);
        struct tevent_threaded_context *tctx;
        int ret;
 
-       ret = tevent_common_wakeup_init(ev);
+       ret = tevent_common_wakeup_init(main_ev);
        if (ret != 0) {
                errno = ret;
                return NULL;
@@ -431,7 +445,7 @@ struct tevent_threaded_context *tevent_threaded_context_create(
                return NULL;
        }
 
-       DLIST_ADD(ev->threaded_contexts, tctx);
+       DLIST_ADD(main_ev->threaded_contexts, tctx);
        talloc_set_destructor(tctx, tevent_threaded_context_destructor);
 
        return tctx;
@@ -458,7 +472,8 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
 {
 #ifdef HAVE_PTHREAD
        const char *create_location = im->create_location;
-       struct tevent_context *ev;
+       struct tevent_context *main_ev = NULL;
+       struct tevent_wrapper_glue *glue = tctx->event_ctx->wrapper.glue;
        int ret, wakeup_fd;
 
        ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
@@ -466,9 +481,7 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
                abort();
        }
 
-       ev = tctx->event_ctx;
-
-       if (ev == NULL) {
+       if (tctx->event_ctx == NULL) {
                /*
                 * Our event context is already gone.
                 */
@@ -489,8 +502,11 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
                abort();
        }
 
+       main_ev = tevent_wrapper_main_ev(tctx->event_ctx);
+
        *im = (struct tevent_immediate) {
-               .event_ctx              = ev,
+               .event_ctx              = tctx->event_ctx,
+               .wrapper                = glue,
                .handler                = handler,
                .private_data           = private_data,
                .handler_name           = handler_name,
@@ -506,15 +522,15 @@ void _tevent_threaded_schedule_immediate(struct tevent_threaded_context *tctx,
         */
        talloc_set_destructor(im, tevent_threaded_schedule_immediate_destructor);
 
-       ret = pthread_mutex_lock(&ev->scheduled_mutex);
+       ret = pthread_mutex_lock(&main_ev->scheduled_mutex);
        if (ret != 0) {
                abort();
        }
 
-       DLIST_ADD_END(ev->scheduled_immediates, im);
-       wakeup_fd = ev->wakeup_fd;
+       DLIST_ADD_END(main_ev->scheduled_immediates, im);
+       wakeup_fd = main_ev->wakeup_fd;
 
-       ret = pthread_mutex_unlock(&ev->scheduled_mutex);
+       ret = pthread_mutex_unlock(&main_ev->scheduled_mutex);
        if (ret != 0) {
                abort();
        }
index cb948d4ba29ef3914a3d2c132d26faa9c12b8c5e..b521f096c48a55fd9cf30b0f10dee59df39f0f59 100644 (file)
@@ -157,6 +157,7 @@ done:
        if (te->busy) {
                return -1;
        }
+       te->wrapper = NULL;
 
        return 0;
 }
@@ -319,6 +320,8 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te,
                                       struct timeval current_time,
                                       bool *removed)
 {
+       struct tevent_context *handler_ev = te->event_ctx;
+
        if (removed != NULL) {
                *removed = false;
        }
@@ -349,13 +352,40 @@ int tevent_common_invoke_timer_handler(struct tevent_timer *te,
         * otherwise we pass the current time
         */
        te->busy = true;
-       te->handler(te->event_ctx, te, current_time, te->private_data);
+       if (te->wrapper != NULL) {
+               handler_ev = te->wrapper->wrap_ev;
+
+               tevent_wrapper_push_use_internal(handler_ev, te->wrapper);
+               te->wrapper->ops->before_timer_handler(
+                                       te->wrapper->wrap_ev,
+                                       te->wrapper->private_state,
+                                       te->wrapper->main_ev,
+                                       te,
+                                       te->next_event,
+                                       current_time,
+                                       te->handler_name,
+                                       te->location);
+       }
+       te->handler(handler_ev, te, current_time, te->private_data);
+       if (te->wrapper != NULL) {
+               te->wrapper->ops->after_timer_handler(
+                                       te->wrapper->wrap_ev,
+                                       te->wrapper->private_state,
+                                       te->wrapper->main_ev,
+                                       te,
+                                       te->next_event,
+                                       current_time,
+                                       te->handler_name,
+                                       te->location);
+               tevent_wrapper_pop_use_internal(handler_ev, te->wrapper);
+       }
        te->busy = false;
 
        tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
                     "Ending timer event %p \"%s\"\n",
                     te, te->handler_name);
 
+       te->wrapper = NULL;
        te->event_ctx = NULL;
        talloc_set_destructor(te, NULL);
        TALLOC_FREE(te);
diff --git a/lib/tevent/tevent_wrapper.c b/lib/tevent/tevent_wrapper.c
new file mode 100644 (file)
index 0000000..05c4c06
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+   Infrastructure for event context wrappers
+
+   Copyright (C) Stefan Metzmacher 2014
+
+     ** 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"
+#ifdef HAVE_PTHREAD
+#include "system/threads.h"
+#endif
+#include "tevent.h"
+#include "tevent_internal.h"
+#include "tevent_util.h"
+
+static int tevent_wrapper_glue_context_init(struct tevent_context *ev)
+{
+       tevent_abort(ev, "tevent_wrapper_glue_context_init() called");
+       errno = ENOSYS;
+       return -1;
+}
+
+static struct tevent_fd *tevent_wrapper_glue_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 tevent_wrapper_glue *glue = ev->wrapper.glue;
+       struct tevent_fd *fde = NULL;
+
+       if (glue->destroyed) {
+               tevent_abort(ev, "add_fd wrapper use after free");
+               return NULL;
+       }
+
+       if (glue->main_ev == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       fde = _tevent_add_fd(glue->main_ev, mem_ctx, fd, flags,
+                            handler, private_data,
+                            handler_name, location);
+       if (fde == NULL) {
+               return NULL;
+       }
+
+       fde->wrapper = glue;
+
+       return fde;
+}
+
+static struct tevent_timer *tevent_wrapper_glue_add_timer(struct tevent_context *ev,
+                                                         TALLOC_CTX *mem_ctx,
+                                                         struct timeval next_event,
+                                                         tevent_timer_handler_t handler,
+                                                         void *private_data,
+                                                         const char *handler_name,
+                                                         const char *location)
+{
+       struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+       struct tevent_timer *te = NULL;
+
+       if (glue->destroyed) {
+               tevent_abort(ev, "add_timer wrapper use after free");
+               return NULL;
+       }
+
+       if (glue->main_ev == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       te = _tevent_add_timer(glue->main_ev, mem_ctx, next_event,
+                              handler, private_data,
+                              handler_name, location);
+       if (te == NULL) {
+               return NULL;
+       }
+
+       te->wrapper = glue;
+
+       return te;
+}
+
+static void tevent_wrapper_glue_schedule_immediate(struct tevent_immediate *im,
+                                                  struct tevent_context *ev,
+                                                  tevent_immediate_handler_t handler,
+                                                  void *private_data,
+                                                  const char *handler_name,
+                                                  const char *location)
+{
+       struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+
+       if (glue->destroyed) {
+               tevent_abort(ev, "scheduke_immediate wrapper use after free");
+               return;
+       }
+
+       if (glue->main_ev == NULL) {
+               tevent_abort(ev, location);
+               errno = EINVAL;
+               return;
+       }
+
+       _tevent_schedule_immediate(im, glue->main_ev,
+                                  handler, private_data,
+                                  handler_name, location);
+
+       im->wrapper = glue;
+
+       return;
+}
+
+static struct tevent_signal *tevent_wrapper_glue_add_signal(struct tevent_context *ev,
+                                                           TALLOC_CTX *mem_ctx,
+                                                           int signum, int sa_flags,
+                                                           tevent_signal_handler_t handler,
+                                                           void *private_data,
+                                                           const char *handler_name,
+                                                           const char *location)
+{
+       struct tevent_wrapper_glue *glue = ev->wrapper.glue;
+       struct tevent_signal *se = NULL;
+
+       if (glue->destroyed) {
+               tevent_abort(ev, "add_signal wrapper use after free");
+               return NULL;
+       }
+
+       if (glue->main_ev == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       se = _tevent_add_signal(glue->main_ev, mem_ctx,
+                               signum, sa_flags,
+                               handler, private_data,
+                               handler_name, location);
+       if (se == NULL) {
+               return NULL;
+       }
+
+       se->wrapper = glue;
+
+       return se;
+}
+
+static int tevent_wrapper_glue_loop_once(struct tevent_context *ev, const char *location)
+{
+       tevent_abort(ev, "tevent_wrapper_glue_loop_once() called");
+       errno = ENOSYS;
+       return -1;
+}
+
+static int tevent_wrapper_glue_loop_wait(struct tevent_context *ev, const char *location)
+{
+       tevent_abort(ev, "tevent_wrapper_glue_loop_wait() called");
+       errno = ENOSYS;
+       return -1;
+}
+
+static const struct tevent_ops tevent_wrapper_glue_ops = {
+       .context_init           = tevent_wrapper_glue_context_init,
+       .add_fd                 = tevent_wrapper_glue_add_fd,
+       .set_fd_close_fn        = tevent_common_fd_set_close_fn,
+       .get_fd_flags           = tevent_common_fd_get_flags,
+       .set_fd_flags           = tevent_common_fd_set_flags,
+       .add_timer              = tevent_wrapper_glue_add_timer,
+       .schedule_immediate     = tevent_wrapper_glue_schedule_immediate,
+       .add_signal             = tevent_wrapper_glue_add_signal,
+       .loop_once              = tevent_wrapper_glue_loop_once,
+       .loop_wait              = tevent_wrapper_glue_loop_wait,
+};
+
+static int tevent_wrapper_context_destructor(struct tevent_context *wrap_ev)
+{
+       struct tevent_wrapper_glue *glue = wrap_ev->wrapper.glue;
+       struct tevent_context *main_ev = NULL;
+       struct tevent_fd *fd = NULL, *fn = NULL;
+       struct tevent_timer *te = NULL, *tn = NULL;
+       struct tevent_immediate *ie = NULL, *in = NULL;
+       struct tevent_signal *se = NULL, *sn = NULL;
+#ifdef HAVE_PTHREAD
+       struct tevent_threaded_context *tctx = NULL, *tctxn = NULL;
+#endif
+
+       if (glue == NULL) {
+               tevent_abort(wrap_ev,
+                       "tevent_wrapper_context_destructor() active on main");
+       }
+
+       if (glue->destroyed && glue->busy) {
+               tevent_common_check_double_free(wrap_ev,
+                       "tevent_context wrapper double free");
+       }
+       glue->destroyed = true;
+
+       if (glue->busy) {
+               return -1;
+       }
+
+       main_ev = glue->main_ev;
+       if (main_ev == NULL) {
+               return 0;
+       }
+
+       tevent_debug(wrap_ev, TEVENT_DEBUG_TRACE,
+                    "Destroying wrapper context %p \"%s\"\n",
+                    wrap_ev, talloc_get_name(glue->private_state));
+
+       glue->main_ev = NULL;
+       DLIST_REMOVE(main_ev->wrapper.list, glue);
+
+#ifdef HAVE_PTHREAD
+       for (tctx = main_ev->threaded_contexts; tctx != NULL; tctx = tctxn) {
+               int ret;
+
+               tctxn = tctx->next;
+
+               if (tctx->event_ctx != glue->wrap_ev) {
+                       continue;
+               }
+
+               ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
+               if (ret != 0) {
+                       abort();
+               }
+
+               /*
+                * Indicate to the thread that the tevent_context is
+                * gone. The counterpart of this is in
+                * _tevent_threaded_schedule_immediate, there we read
+                * this under the threaded_context's mutex.
+                */
+
+               tctx->event_ctx = NULL;
+
+               ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
+               if (ret != 0) {
+                       abort();
+               }
+
+               DLIST_REMOVE(main_ev->threaded_contexts, tctx);
+       }
+#endif
+
+       for (fd = main_ev->fd_events; fd; fd = fn) {
+               fn = fd->next;
+
+               if (fd->wrapper != glue) {
+                       continue;
+               }
+
+               tevent_fd_set_flags(fd, 0);
+
+               fd->wrapper = NULL;
+               fd->event_ctx = NULL;
+               DLIST_REMOVE(main_ev->fd_events, fd);
+       }
+
+       for (te = main_ev->timer_events; te; te = tn) {
+               tn = te->next;
+
+               if (te->wrapper != glue) {
+                       continue;
+               }
+
+               te->wrapper = NULL;
+               te->event_ctx = NULL;
+
+               if (main_ev->last_zero_timer == te) {
+                       main_ev->last_zero_timer = DLIST_PREV(te);
+               }
+               DLIST_REMOVE(main_ev->timer_events, te);
+       }
+
+       for (ie = main_ev->immediate_events; ie; ie = in) {
+               in = ie->next;
+
+               if (ie->wrapper != glue) {
+                       continue;
+               }
+
+               ie->wrapper = NULL;
+               ie->event_ctx = NULL;
+               ie->cancel_fn = NULL;
+               DLIST_REMOVE(main_ev->immediate_events, ie);
+       }
+
+       for (se = main_ev->signal_events; se; se = sn) {
+               sn = se->next;
+
+               if (se->wrapper != glue) {
+                       continue;
+               }
+
+               se->wrapper = NULL;
+               tevent_cleanup_pending_signal_handlers(se);
+       }
+
+       return 0;
+}
+
+struct tevent_context *_tevent_context_wrapper_create(struct tevent_context *main_ev,
+                                               TALLOC_CTX *mem_ctx,
+                                               const struct tevent_wrapper_ops *ops,
+                                               void *pstate,
+                                               size_t psize,
+                                               const char *type,
+                                               const char *location)
+{
+       void **ppstate = (void **)pstate;
+       struct tevent_context *ev = NULL;
+
+       if (main_ev->wrapper.glue != NULL) {
+               /*
+                * stacking of wrappers is not supported
+                */
+               tevent_debug(main_ev->wrapper.glue->main_ev, TEVENT_DEBUG_FATAL,
+                            "%s: %s() stacking not allowed\n",
+                            __func__, location);
+               errno = EINVAL;
+               return NULL;
+       }
+
+       if (main_ev->nesting.allowed) {
+               /*
+                * wrappers conflict with nesting
+                */
+               tevent_debug(main_ev->wrapper.glue->main_ev, TEVENT_DEBUG_FATAL,
+                            "%s: %s() conflicts with nesting\n",
+                            __func__, location);
+               errno = EINVAL;
+               return NULL;
+       }
+
+       ev = talloc_zero(mem_ctx, struct tevent_context);
+       if (ev == NULL) {
+               return NULL;
+       }
+       ev->ops = &tevent_wrapper_glue_ops;
+
+       ev->wrapper.glue = talloc_zero(ev, struct tevent_wrapper_glue);
+       if (ev->wrapper.glue == NULL) {
+               talloc_free(ev);
+               return NULL;
+       }
+
+       talloc_set_destructor(ev, tevent_wrapper_context_destructor);
+
+       ev->wrapper.glue->wrap_ev = ev;
+       ev->wrapper.glue->main_ev = main_ev;
+       ev->wrapper.glue->ops = ops;
+       ev->wrapper.glue->private_state = talloc_size(ev->wrapper.glue, psize);
+       if (ev->wrapper.glue->private_state == NULL) {
+               talloc_free(ev);
+               return NULL;
+       }
+       talloc_set_name_const(ev->wrapper.glue->private_state, type);
+
+       DLIST_ADD_END(main_ev->wrapper.list, ev->wrapper.glue);
+
+       *ppstate = ev->wrapper.glue->private_state;
+       return ev;
+}
+
+bool tevent_context_is_wrapper(struct tevent_context *ev)
+{
+       if (ev->wrapper.glue != NULL) {
+               return true;
+       }
+
+       return false;
+}
+
+_PRIVATE_
+struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev)
+{
+       if (ev == NULL) {
+               return NULL;
+       }
+
+       if (ev->wrapper.glue == NULL) {
+               return ev;
+       }
+
+       return ev->wrapper.glue->main_ev;
+}
+
+/*
+ * 32 stack elements should be more than enough
+ *
+ * e.g. Samba uses just 8 elements for [un]become_{root,user}()
+ */
+#define TEVENT_WRAPPER_STACK_SIZE 32
+
+static struct tevent_wrapper_stack {
+       const void *ev_ptr;
+       const struct tevent_wrapper_glue *wrapper;
+} wrapper_stack[TEVENT_WRAPPER_STACK_SIZE];
+
+static size_t wrapper_stack_idx;
+
+_PRIVATE_
+void tevent_wrapper_push_use_internal(struct tevent_context *ev,
+                                     struct tevent_wrapper_glue *wrapper)
+{
+       /*
+        * ev and wrapper need to belong together!
+        * It's also fine to only have a raw ev
+        * without a wrapper.
+        */
+       if (unlikely(ev->wrapper.glue != wrapper)) {
+               tevent_abort(ev, "tevent_wrapper_push_use_internal() invalid arguments");
+               return;
+       }
+
+       if (wrapper != NULL) {
+               if (unlikely(wrapper->busy)) {
+                       tevent_abort(ev, "wrapper already busy!");
+                       return;
+               }
+               wrapper->busy = true;
+       }
+
+       if (unlikely(wrapper_stack_idx >= TEVENT_WRAPPER_STACK_SIZE)) {
+               tevent_abort(ev, "TEVENT_WRAPPER_STACK_SIZE overflow");
+               return;
+       }
+
+       wrapper_stack[wrapper_stack_idx] = (struct tevent_wrapper_stack) {
+               .ev_ptr = ev,
+               .wrapper = wrapper,
+       };
+       wrapper_stack_idx++;
+}
+
+_PRIVATE_
+void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr,
+                                    struct tevent_wrapper_glue *wrapper)
+{
+       struct tevent_context *main_ev = NULL;
+
+       /*
+        * Note that __ev_ptr might a a stale pointer and should not
+        * be touched, we just compare the pointer value in order
+        * to enforce the stack order.
+        */
+
+       if (wrapper != NULL) {
+               main_ev = wrapper->main_ev;
+       }
+
+       if (unlikely(wrapper_stack_idx == 0)) {
+               tevent_abort(main_ev, "tevent_wrapper stack already empty");
+               return;
+       }
+       wrapper_stack_idx--;
+
+       if (wrapper != NULL) {
+               wrapper->busy = false;
+       }
+
+       if (wrapper_stack[wrapper_stack_idx].ev_ptr != __ev_ptr) {
+               tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch ev!");
+               return;
+       }
+       if (wrapper_stack[wrapper_stack_idx].wrapper != wrapper) {
+               tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch wrap!");
+               return;
+       }
+
+       if (wrapper == NULL) {
+               return;
+       }
+
+       if (wrapper->destroyed) {
+               /*
+                * Notice that we can't use TALLOC_FREE()
+                * here because wrapper is a talloc child
+                * of wrapper->wrap_ev.
+                */
+               talloc_free(wrapper->wrap_ev);
+       }
+}
+
+bool _tevent_context_push_use(struct tevent_context *ev,
+                             const char *location)
+{
+       bool ok;
+
+       if (ev->wrapper.glue == NULL) {
+               tevent_wrapper_push_use_internal(ev, NULL);
+               return true;
+       }
+
+       if (ev->wrapper.glue->main_ev == NULL) {
+               return false;
+       }
+
+       tevent_wrapper_push_use_internal(ev, ev->wrapper.glue);
+       ok = ev->wrapper.glue->ops->before_use(ev->wrapper.glue->wrap_ev,
+                                              ev->wrapper.glue->private_state,
+                                              ev->wrapper.glue->main_ev,
+                                              location);
+       if (!ok) {
+               tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
+               return false;
+       }
+
+       return true;
+}
+
+void _tevent_context_pop_use(struct tevent_context *ev,
+                            const char *location)
+{
+       tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
+
+       if (ev->wrapper.glue == NULL) {
+               return;
+       }
+
+       if (ev->wrapper.glue->main_ev == NULL) {
+               return;
+       }
+
+       ev->wrapper.glue->ops->after_use(ev->wrapper.glue->wrap_ev,
+                                        ev->wrapper.glue->private_state,
+                                        ev->wrapper.glue->main_ev,
+                                        location);
+}
+
+bool tevent_context_same_loop(struct tevent_context *ev1,
+                             struct tevent_context *ev2)
+{
+       struct tevent_context *main_ev1 = tevent_wrapper_main_ev(ev1);
+       struct tevent_context *main_ev2 = tevent_wrapper_main_ev(ev2);
+
+       if (main_ev1 == NULL) {
+               return false;
+       }
+
+       if (main_ev1 == main_ev2) {
+               return true;
+       }
+
+       return false;
+}
index 94d190f3b60555161dd7f3488b88881b92708aa8..2395ead9aa93b5b2a5dc8010067b2ae7a28f2b9b 100644 (file)
@@ -77,7 +77,7 @@ def build(bld):
     bld.RECURSE('lib/talloc')
 
     SRC = '''tevent.c tevent_debug.c tevent_fd.c tevent_immediate.c
-             tevent_queue.c tevent_req.c
+             tevent_queue.c tevent_req.c tevent_wrapper.c
              tevent_poll.c tevent_threads.c
              tevent_signal.c tevent_standard.c tevent_timed.c tevent_util.c tevent_wakeup.c'''