tevent: add tevent_req_defer_callback()
authorStefan Metzmacher <metze@samba.org>
Fri, 8 Jul 2011 13:54:51 +0000 (15:54 +0200)
committerStefan Metzmacher <metze@samba.org>
Sat, 9 Jul 2011 09:02:42 +0000 (11:02 +0200)
metze

lib/tevent/tevent.h
lib/tevent/tevent_internal.h
lib/tevent/tevent_req.c

index 7f9f72a57aebf3b74458cd2d54552acf931410bd..7ad566c69ea08da73c243401df39ba5a30a15a3a 100644 (file)
@@ -1047,6 +1047,48 @@ void _tevent_req_oom(struct tevent_req *req,
 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.
  *
index 9227f90315631ce3daf151aeb834ec24077d6b99..472beb5c94ab08a6a3608549ad2f5e35c4011d71 100644 (file)
@@ -141,6 +141,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
                 *
index 92697b7df958f71454c1ac623fc497c7672d5fe4..d8d0c5f56455492979635d1a235f5be6db5f7ccc 100644 (file)
@@ -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);
        }
@@ -169,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) {