tevent: preferr the write handler if there're two possible handlers registered with...
[sfrench/samba-autobuild/.git] / lib / tevent / tevent_queue.c
index 6c8fbe4f95f0acf6042f72cdd04b29b1f9ea71de..4750675802f26f6548e9c76e9b1c13933078a220 100644 (file)
@@ -34,6 +34,7 @@ struct tevent_queue_entry {
        bool triggered;
 
        struct tevent_req *req;
+       struct tevent_context *ev;
 
        tevent_queue_trigger_fn_t trigger;
        void *private_data;
@@ -44,12 +45,16 @@ struct tevent_queue {
        const char *location;
 
        bool running;
-       struct tevent_timer *timer;
+       struct tevent_immediate *immediate;
 
        size_t length;
        struct tevent_queue_entry *list;
 };
 
+static void tevent_queue_immediate_trigger(struct tevent_context *ev,
+                                          struct tevent_immediate *im,
+                                          void *private_data);
+
 static int tevent_queue_entry_destructor(struct tevent_queue_entry *e)
 {
        struct tevent_queue *q = e->queue;
@@ -61,14 +66,23 @@ static int tevent_queue_entry_destructor(struct tevent_queue_entry *e)
        DLIST_REMOVE(q->list, e);
        q->length--;
 
-       if (e->triggered &&
-           q->running &&
-           q->list) {
-               q->list->triggered = true;
-               q->list->trigger(q->list->req,
-                                q->list->private_data);
+       if (!q->running) {
+               return 0;
+       }
+
+       if (!q->list) {
+               return 0;
+       }
+
+       if (q->list->triggered) {
+               return 0;
        }
 
+       tevent_schedule_immediate(q->immediate,
+                                 q->list->ev,
+                                 tevent_queue_immediate_trigger,
+                                 q);
+
        return 0;
 }
 
@@ -100,6 +114,11 @@ struct tevent_queue *_tevent_queue_create(TALLOC_CTX *mem_ctx,
                talloc_free(queue);
                return NULL;
        }
+       queue->immediate = tevent_create_immediate(queue);
+       if (!queue->immediate) {
+               talloc_free(queue);
+               return NULL;
+       }
 
        queue->location = location;
 
@@ -110,89 +129,172 @@ struct tevent_queue *_tevent_queue_create(TALLOC_CTX *mem_ctx,
        return queue;
 }
 
-static void tevent_queue_timer_start(struct tevent_context *ev,
-                                    struct tevent_timer *te,
-                                    struct timeval now,
-                                    void *private_data)
+static void tevent_queue_immediate_trigger(struct tevent_context *ev,
+                                          struct tevent_immediate *im,
+                                          void *private_data)
 {
        struct tevent_queue *q = talloc_get_type(private_data,
                                  struct tevent_queue);
 
-       talloc_free(te);
-       q->timer = NULL;
+       if (!q->running) {
+               return;
+       }
 
        q->list->triggered = true;
        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;
        e->req = req;
+       e->ev = ev;
        e->trigger = trigger;
        e->private_data = private_data;
 
-       if (queue->running &&
-           !queue->timer &&
-           !queue->list) {
-               queue->timer = tevent_add_timer(ev, queue, tevent_timeval_zero(),
-                                               tevent_queue_timer_start,
-                                               queue);
-               if (!queue->timer) {
-                       talloc_free(e);
-                       return false;
-               }
+       /*
+        * 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 e;
+       }
+
+       if (queue->list->triggered) {
+               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,
+                                 queue->list->ev,
+                                 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;
 }
 
-bool tevent_queue_start(struct tevent_queue *queue,
-                       struct tevent_context *ev)
+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) {
                /* already started */
-               return true;
+               return;
        }
 
-       if (!queue->timer &&
-           queue->list) {
-               queue->timer = tevent_add_timer(ev, queue, tevent_timeval_zero(),
-                                               tevent_queue_timer_start,
-                                               queue);
-               if (!queue->timer) {
-                       return false;
-               }
+       queue->running = true;
+
+       if (!queue->list) {
+               return;
        }
 
-       queue->running = true;
+       if (queue->list->triggered) {
+               return;
+       }
 
-       return true;
+       tevent_schedule_immediate(queue->immediate,
+                                 queue->list->ev,
+                                 tevent_queue_immediate_trigger,
+                                 queue);
 }
 
 void tevent_queue_stop(struct tevent_queue *queue)
 {
        queue->running = false;
-       talloc_free(queue->timer);
-       queue->timer = NULL;
 }
 
 size_t tevent_queue_length(struct tevent_queue *queue)
 {
        return queue->length;
 }
+
+bool tevent_queue_running(struct tevent_queue *queue)
+{
+       return queue->running;
+}