tevent: Call depth tracking
authorPavel Filipenský <pfilipen@redhat.com>
Thu, 16 Jun 2022 14:23:22 +0000 (16:23 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 18 Jan 2023 16:26:36 +0000 (16:26 +0000)
The change to lib/tevent/ABI/tevent-0.13.0.sigs will be reverted
in the commit for the 0.14.0 release...

Signed-off-by: Pavel Filipenský <pfilipen@redhat.com>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
lib/tevent/ABI/tevent-0.13.0.sigs
lib/tevent/tevent.c
lib/tevent/tevent.h
lib/tevent/tevent_debug.c
lib/tevent/tevent_internal.h
lib/tevent/tevent_queue.c
lib/tevent/tevent_req.c

index 76c84af812cd2bf341ca82bc86d327bf50e16321..f2a11902a8091861640ce8cbe27c1802fafdfa97 100644 (file)
@@ -129,6 +129,10 @@ tevent_set_trace_timer_callback: void (struct tevent_context *, tevent_trace_tim
 tevent_signal_get_tag: uint64_t (const struct tevent_signal *)
 tevent_signal_set_tag: void (struct tevent_signal *, uint64_t)
 tevent_signal_support: bool (struct tevent_context *)
+tevent_thread_call_depth_activate: void (size_t *)
+tevent_thread_call_depth_deactivate: void (void)
+tevent_thread_call_depth_reset_from_req: void (struct tevent_req *)
+tevent_thread_call_depth_start: void (struct tevent_req *)
 tevent_thread_proxy_create: struct tevent_thread_proxy *(struct tevent_context *)
 tevent_thread_proxy_schedule: void (struct tevent_thread_proxy *, struct tevent_immediate **, tevent_immediate_handler_t, void *)
 tevent_threaded_context_create: struct tevent_threaded_context *(TALLOC_CTX *, struct tevent_context *)
index 19f30ff77225f3e0f6a427522d07cb03e0d170ad..698e467c88c11a8051599efc2a1232ae268da895 100644 (file)
@@ -823,6 +823,9 @@ int _tevent_loop_once(struct tevent_context *ev, const char *location)
        ret = ev->ops->loop_once(ev, location);
        tevent_trace_point_callback(ev, TEVENT_TRACE_AFTER_LOOP_ONCE);
 
+       /* New event (and request) will always start with call depth 0. */
+       tevent_thread_call_depth_set(0);
+
        if (ev->nesting.level > 0) {
                if (ev->nesting.hook_fn) {
                        int ret2;
index 350270d8e407ed97bd51d84bcf0f49991f6d87b9..303918528850ab53d74a4f1a0589977ffea7390a 100644 (file)
@@ -1928,6 +1928,169 @@ pid_t tevent_cached_getpid(void);
 /* @} */
 
 
+/**
+ * @defgroup tevent_thread_call_depth The tevent call depth tracking functions
+ * @ingroup tevent
+ *
+ *
+ * The call depth tracking consists of two parts.
+ *
+ * Part 1 - storing the depth inside each tevent request.
+ *
+ * Each instance of 'struct tevent_req' internally stores the value of the
+ * current depth. If a new subrequest is created via tevent_req_create(), the
+ * newly created subrequest gets the value from the parent incremented by 1.
+ *
+ * Part 2 - updating external variable with the call depth of the currently
+ * processed tevent request.
+ *
+ * The intended use of call depth is for the trace indentation.  This is done
+ * by registering the address of an external size_t variable via
+ * tevent_thread_call_depth_activate(). And the tracing code just reads it's
+ * value.
+ *
+ * The updates happen during:
+ *
+ * tevent_req_create()
+ * - external variable is set to the value of the newly created request (i.e.
+ *   value of the parent incremented by 1)
+ *
+ * tevent_req_notify_callback()
+ * - external variable is set to the value of the parent tevent request, which
+ *   is just about to be processed
+ *
+ * tevent_queue_immediate_trigger()
+ * - external variable is set to the value of the request coming from the queue
+ *
+ *
+ * While 'Part 1' maintains the call depth value inside each teven request
+ * precisely, the value of the external variable depends on the call flow and
+ * can be changed after return from a function call, so it no longer matches
+ * the value of the request being processed in the current function.
+ *
+ * @code
+ * struct tevent_req *foo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+ * {
+ *     struct tevent_req *req, *subreq;
+ *     struct foo_state *state;
+ *
+ *     // External variable has value 'X', which is the value in parent code
+ *     // It is ok, since tracing starts often only after tevent_req_create()
+ *     req = tevent_req_create(mem_ctx, &state, struct foo_state);
+ *
+ *     // External variable has now value 'X + 1'
+ *     D_DEBUG("foo_send(): the external variable has the expected value\n");
+ *
+ *     subreq = bar_send(state, ev, ...);
+ *     tevent_req_set_callback(subreq, foo_done, req);
+ *
+ *     // External variable has value 'X + 1 + n', where n > 0 and n is the
+ *     // depth reached in bar_send().
+ *     // We want to reset it via tevent_thread_call_depth_reset_from_req(),
+ *     // since we want the following D_DEBUG() to have the right trace
+ *     //indentation.
+ *
+ *     tevent_thread_call_depth_reset_from_req(req);
+ *     // External variable has again value 'X + 1' taken from req.
+ *     D_DEBUG("foo_send(): the external variable has the expected value\n");
+ *     return req;
+ * }
+ *
+ * static void foo_done(struct tevent_req *subreq)
+ * {
+ *     struct tevent_req *req =
+ *         tevent_req_callback_data(subreq,
+ *         struct tevent_req);
+ *     struct foo_state *state =
+ *         tevent_req_data(req,
+ *         struct foo_state);
+ *
+ *     // external variable has value 'X + 1'
+ *
+ *     D_DEBUG("foo_done(): the external variable has the expected value\n");
+ *     status = bar_recv(subreq, state, ...)
+ *     tevent_req_done(req);
+ * }
+ *
+ * NTSTATUS foo_recv(struct tevent_req *req)
+ * {
+ *     struct foo_state *state = tevent_req_data( req, struct foo_state);
+ *
+ *     // external variable has value 'X' (not 'X + 1')
+ *     // which is ok, if we consider _recv() to be an access function
+ *     // called from the parent context
+ *
+ *     D_DEBUG("foo_recv(): external variable has the value from parent\n");
+ *     return NT_STATUS_OK;
+ * }
+ * @endcode
+ *
+ * Interface has 3 parts:
+ *
+ * Part 1: activation/deactivation
+ *
+ * tevent_thread_call_depth_activate(), tevent_thread_call_depth_deactivate()
+ *
+ * Activating registers external size_t variable that will be maintained with
+ * the current call depth.
+ *
+ * Part 2: Mark the request (and its subrequests) to be tracked
+ *
+ * tevent_thread_call_depth_start(struct tevent_req *req)
+ *
+ * By default, all newly created requests have call depth set to 0.
+ * tevent_thread_call_depth_start() should be called shortly after
+ * tevent_req_create(). It sets the call depth to 1.
+ * Subrequest will have call depth 2 and so on.
+ *
+ * Part 3: reset the external variable using value from tevent request
+ *
+ * tevent_thread_call_depth_reset_from_req(struct tevent_req *req)
+ *
+ * If the call depth is used for trace indentation, it might be usefull to
+ * reset the external variable to the call depth of currently processed tevent
+ * request, since the ext. variable can be changed after return from a function
+ * call that has created subrequests.
+ *
+ * THREADING
+ *
+ * The state is thread specific, i.e. each thread can activate it and register
+ * its own external variable.
+ *
+ * @{
+ */
+
+/**
+ * Activate call depth tracking and register external variable that will
+ * be updated to the call epth of currenty processed tevent request.
+ *
+ * @param[in]  ptr   Address of external variable
+ */
+void tevent_thread_call_depth_activate(size_t *ptr);
+
+/**
+ * Deactivate call depth tracking. Can be used in the child process,
+ * after fork.
+ */
+void tevent_thread_call_depth_deactivate(void);
+
+/**
+ * This request will have call depth set to 1, its subrequest will get 2 and so
+ * on. All other requests will have call depth 0.
+ */
+void tevent_thread_call_depth_start(struct tevent_req *req);
+
+/**
+ * Set the external variable to the call depth of the request req.
+ *
+ * @param[in]  req   Request from which the call depth is assigned to ext.
+ * variable.
+ */
+void tevent_thread_call_depth_reset_from_req(struct tevent_req *req);
+
+/* @} */
+
+
 /**
  * @defgroup tevent_queue The tevent queue functions
  * @ingroup tevent
index 6b8f2d5100bb009be9dfd06cb655e72a6f09dfa7..6b04089e85b7cf1b68d61435acdd863282acd787 100644 (file)
@@ -292,3 +292,41 @@ void tevent_trace_queue_callback(struct tevent_context *ev,
                ev->tracing.qe.callback(qe, tp, ev->tracing.qe.private_data);
        }
 }
+
+static __thread size_t *tevent_thread_call_depth_ptr = NULL;
+
+void tevent_thread_call_depth_activate(size_t *ptr)
+{
+       tevent_thread_call_depth_ptr = ptr;
+       *tevent_thread_call_depth_ptr = 0;
+}
+
+void tevent_thread_call_depth_deactivate(void)
+{
+       /* Reset the previous storage */
+       if (tevent_thread_call_depth_ptr != NULL) {
+               *tevent_thread_call_depth_ptr = 0;
+       }
+       tevent_thread_call_depth_ptr = NULL;
+}
+
+void tevent_thread_call_depth_start(struct tevent_req *req)
+{
+       if (tevent_thread_call_depth_ptr != NULL) {
+               *tevent_thread_call_depth_ptr = req->internal.call_depth = 1;
+       }
+}
+
+void tevent_thread_call_depth_reset_from_req(struct tevent_req *req)
+{
+       if (tevent_thread_call_depth_ptr != NULL) {
+               *tevent_thread_call_depth_ptr = req->internal.call_depth;
+       }
+}
+
+_PRIVATE_ void tevent_thread_call_depth_set(size_t depth)
+{
+       if (tevent_thread_call_depth_ptr != NULL) {
+               *tevent_thread_call_depth_ptr = depth;
+       }
+}
index a5df1b14b26cb9cf608602ead1c6a29398d7b5fe..e6853d148eec28b96f3dcf2d07899e7eb817940d 100644 (file)
@@ -169,6 +169,8 @@ struct tevent_req {
                 * @brief The place where profiling data is kept
                 */
                struct tevent_req_profile *profile;
+
+               size_t call_depth;
        } internal;
 };
 
@@ -497,6 +499,7 @@ void tevent_epoll_set_panic_fallback(struct tevent_context *ev,
                                               bool replay));
 #endif
 
+void tevent_thread_call_depth_set(size_t depth);
 
 void tevent_trace_point_callback(struct tevent_context *ev,
                                 enum tevent_trace_point);
index dffe30fdcfb0f7dad13c6b9a313a10cbd9125720..647b8a00ed342eef77585cf0aefd9ccbc59bb1ea 100644 (file)
@@ -149,6 +149,8 @@ static void tevent_queue_immediate_trigger(struct tevent_context *ev,
 
        tevent_trace_queue_callback(ev, q->list,
                                    TEVENT_EVENT_TRACE_BEFORE_HANDLER);
+       /* Set the call depth of the request coming from the queue. */
+       tevent_thread_call_depth_set(q->list->req->internal.call_depth);
        q->list->triggered = true;
        q->list->trigger(q->list->req, q->list->private_data);
 }
index 7821d9ae7341f958d46aa283e92025d4331fedac..e76863b2170c98c4d86a4bbc15cb090b2eb5d14e 100644 (file)
@@ -120,6 +120,13 @@ struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx,
        }
 
        *ppdata = data;
+
+       /* Initially, talloc_zero_size() sets internal.call_depth to 0 */
+       if (parent != NULL && parent->internal.call_depth > 0) {
+               req->internal.call_depth = parent->internal.call_depth + 1;
+               tevent_thread_call_depth_set(req->internal.call_depth);
+       }
+
        return req;
 }
 
@@ -138,6 +145,9 @@ void _tevent_req_notify_callback(struct tevent_req *req, const char *location)
                return;
        }
        if (req->async.fn != NULL) {
+               /* Calling back the parent code, decrement the call depth. */
+               tevent_thread_call_depth_set(req->internal.call_depth > 0 ?
+                                            req->internal.call_depth - 1 : 0);
                req->async.fn(req);
        }
 }