tevent: add tevent_req_set_cleanup_fn()
authorStefan Metzmacher <metze@samba.org>
Fri, 27 Sep 2013 00:29:57 +0000 (02:29 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 17 Jan 2014 11:38:08 +0000 (12:38 +0100)
Note that some callers used their own destructor for their
tevent_req instance, they'll just overwrite this,
which is not intended, but works without problems.

The intended way is to specify a cleanup function
and handle the TEVENT_REQ_RECEIVED state as destructor.

Note that the TEVENT_REQ_RECEIVED cleanup event might
be triggered by an explicit tevent_req_received()
in the _recv() function. The TEVENT_REQ_RECEIVED event
is only triggered once as tevent_req_received()
will remove the destructor.

So the difference compared to a custom destructor
is that the struct tevent_req itself can continue
to be there, while tevent_req_received() removed
all internal state.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
lib/tevent/tevent.h
lib/tevent/tevent_internal.h
lib/tevent/tevent_req.c

index d7d4f19effdb2f4c4ededd8cca1a431be9701cb1..c54cbe2133649df5830ea0fd61f766fafd7d64d4 100644 (file)
@@ -934,6 +934,41 @@ bool _tevent_req_cancel(struct tevent_req *req, const char *location);
        _tevent_req_cancel(req, __location__)
 #endif
 
+/**
+ * @brief A typedef for a cleanup function for a tevent request.
+ *
+ * @param[in]  req       The tevent request calling this function.
+ *
+ * @param[in]  req_state The current tevent_req_state.
+ *
+ */
+typedef void (*tevent_req_cleanup_fn)(struct tevent_req *req,
+                                     enum tevent_req_state req_state);
+
+/**
+ * @brief This function sets a cleanup function for the given tevent request.
+ *
+ * This function can be used to setup a cleanup function for the given request.
+ * This will be triggered when the tevent_req_done() or tevent_req_error()
+ * function was called, before notifying the callers callback function,
+ * and also before scheduling the deferred trigger.
+ *
+ * This might be useful if more than one tevent_req belong together
+ * and need to finish both requests at the same time.
+ *
+ * The cleanup function is able to call tevent_req_done() or tevent_req_error()
+ * recursively, the cleanup function is only triggered the first time.
+ *
+ * The cleanup function is also called by tevent_req_received()
+ * (possibly triggered from tevent_req_destructor()) before destroying
+ * the private data of the tevent_req.
+ *
+ * @param[in]  req      The request to use.
+ *
+ * @param[in]  fn       A pointer to the cancel function.
+ */
+void tevent_req_set_cleanup_fn(struct tevent_req *req, tevent_req_cleanup_fn fn);
+
 #ifdef DOXYGEN
 /**
  * @brief Create an async tevent request.
index df7328864d9cfceeac7ed21d84c5551491e703b0..d25dc050e35853cc7e33b5838eb58e0301a7717e 100644 (file)
@@ -73,6 +73,18 @@ struct tevent_req {
         */
        tevent_req_cancel_fn private_cancel;
 
+       /**
+        * @brief A function to cleanup the request
+        *
+        * The implementation might want to set a function
+        * that is called before the tevent_req_done() and tevent_req_error()
+        * trigger the callers callback function.
+        */
+       struct {
+               tevent_req_cleanup_fn fn;
+               enum tevent_req_state state;
+       } private_cleanup;
+
        /**
         * @brief Internal state of the request
         *
index 30e91e252691708a7765273f319d580bf97c4291..c86fb68f8013dc4270ad563bbed5b7cb9be12800 100644 (file)
@@ -113,6 +113,24 @@ void _tevent_req_notify_callback(struct tevent_req *req, const char *location)
        }
 }
 
+static void tevent_req_cleanup(struct tevent_req *req)
+{
+       if (req->private_cleanup.fn == NULL) {
+               return;
+       }
+
+       if (req->private_cleanup.state >= req->internal.state) {
+               /*
+                * Don't call the cleanup_function multiple times for the same
+                * state recursively
+                */
+               return;
+       }
+
+       req->private_cleanup.state = req->internal.state;
+       req->private_cleanup.fn(req, req->internal.state);
+}
+
 static void tevent_req_finish(struct tevent_req *req,
                              enum tevent_req_state state,
                              const char *location)
@@ -124,6 +142,10 @@ static void tevent_req_finish(struct tevent_req *req,
        TALLOC_FREE(req->internal.timer);
 
        req->internal.state = state;
+       req->internal.finish_location = location;
+
+       tevent_req_cleanup(req);
+
        _tevent_req_notify_callback(req, location);
 }
 
@@ -220,6 +242,8 @@ void tevent_req_received(struct tevent_req *req)
 
        req->internal.state = TEVENT_REQ_RECEIVED;
 
+       tevent_req_cleanup(req);
+
        TALLOC_FREE(req->data);
 }
 
@@ -315,3 +339,9 @@ bool _tevent_req_cancel(struct tevent_req *req, const char *location)
 
        return req->private_cancel(req);
 }
+
+void tevent_req_set_cleanup_fn(struct tevent_req *req, tevent_req_cleanup_fn fn)
+{
+       req->private_cleanup.state = req->internal.state;
+       req->private_cleanup.fn = fn;
+}