]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Push/pop global event stack automatically when ioloop contexts are switched
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 11 Mar 2021 00:19:37 +0000 (02:19 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Wed, 29 Sep 2021 10:09:58 +0000 (10:09 +0000)
src/lib/ioloop-private.h
src/lib/ioloop.c
src/lib/lib-event.h
src/lib/test-ioloop.c

index abf2a44a3faf2ceb7445036a59268645921b589f..b7b2aafece7835f56f7e8f21fab7ec86d7ae3eab 100644 (file)
@@ -104,6 +104,8 @@ struct ioloop_context {
        int refcount;
        struct ioloop *ioloop;
        ARRAY(struct ioloop_context_callback) callbacks;
+       ARRAY(struct event *) global_event_stack;
+       struct event *root_global_event;
 };
 
 int io_loop_run_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r);
index db68b7e4549783163192f66aa780ca15b3d14e8e..c1db61a2f23217b7d4eb1867c25a12a3a7dbeb47 100644 (file)
@@ -1071,6 +1071,7 @@ void io_loop_context_unref(struct ioloop_context **_ctx)
        i_assert(ctx->ioloop->cur_ctx != ctx);
 
        array_free(&ctx->callbacks);
+       array_free(&ctx->global_event_stack);
        i_free(ctx);
 }
 
@@ -1127,6 +1128,48 @@ io_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx)
        }
 }
 
+static void io_loop_context_push_global_events(struct ioloop_context *ctx)
+{
+       struct event *const *events;
+       unsigned int i, count;
+
+       ctx->root_global_event = event_get_global();
+
+       if (!array_is_created(&ctx->global_event_stack))
+               return;
+
+       /* push the global events from stack in reverse order */
+       events = array_get(&ctx->global_event_stack, &count);
+       if (count == 0)
+               return;
+
+       /* Remember the oldest global event. We're going to pop until that
+          event when deactivating the context. */
+       for (i = count; i > 0; i--)
+               event_push_global(events[i-1]);
+       array_clear(&ctx->global_event_stack);
+}
+
+static void io_loop_context_pop_global_events(struct ioloop_context *ctx)
+{
+       struct event *event;
+
+       /* ioloop context is always global, so we can't push one ioloop context
+          on top of another one. We'll need to rewind the global event stack
+          until we've reached the event that started this context. We'll push
+          these global events back when the ioloop context is activated
+          again. (We'll assert-crash if the root event is freed before these
+          global events have been popped.) */
+       while ((event = event_get_global()) != ctx->root_global_event) {
+               i_assert(event != NULL);
+               if (!array_is_created(&ctx->global_event_stack))
+                       i_array_init(&ctx->global_event_stack, 4);
+               array_push_back(&ctx->global_event_stack, &event);
+               event_pop_global(event);
+       }
+       ctx->root_global_event = NULL;
+}
+
 void io_loop_context_activate(struct ioloop_context *ctx)
 {
        struct ioloop_context_callback *cb;
@@ -1134,6 +1177,7 @@ void io_loop_context_activate(struct ioloop_context *ctx)
        i_assert(ctx->ioloop->cur_ctx == NULL);
 
        ctx->ioloop->cur_ctx = ctx;
+       io_loop_context_push_global_events(ctx);
        io_loop_context_ref(ctx);
        array_foreach_modifiable(&ctx->callbacks, cb) {
                i_assert(!cb->activated);
@@ -1160,6 +1204,7 @@ void io_loop_context_deactivate(struct ioloop_context *ctx)
                }
        }
        ctx->ioloop->cur_ctx = NULL;
+       io_loop_context_pop_global_events(ctx);
        io_loop_context_remove_deleted_callbacks(ctx);
        io_loop_context_unref(&ctx);
 }
index 7d1b6da0306f1dbe22a057ce23101d646bdf626e..c7ea4ef13d310340ad461b4d78344184da1d41dd 100644 (file)
@@ -185,6 +185,11 @@ void event_unref(struct event **event);
    The global event works the same as if all the events' roots were instead
    pointing to the global event. Global events don't affect log prefixes.
 
+   If ioloop contexts are used, the global events will automatically follow the
+   contexts. Any global events pushed while running in a context are popped
+   out when the context is deactivated, and pushed back when context is
+   activated again.
+
    The created global events should use event_get_global() as their parent
    event. Only the last pushed global event is used. */
 struct event *event_push_global(struct event *event);
index b0ab89387cbb9aaad448485bf976d2da8748a582..89f688872c760189115dcb5f2aea70a6929f5c5a 100644 (file)
@@ -325,6 +325,72 @@ static void test_ioloop_context(void)
        test_end();
 }
 
+static void test_ioloop_context_events_run(struct event *root_event)
+{
+       struct ioloop *ioloop = io_loop_create();
+       struct ioloop_context *ctx1, *ctx2;
+
+       /* create context 1 */
+       ctx1 = io_loop_context_new(ioloop);
+       io_loop_context_switch(ctx1);
+       struct event *ctx1_event1 = event_create(NULL);
+       event_push_global(ctx1_event1);
+       struct event *ctx1_event2 = event_create(NULL);
+       event_push_global(ctx1_event2);
+       io_loop_context_deactivate(ctx1);
+
+       test_assert(event_get_global() == root_event);
+
+       /* create context 2 */
+       ctx2 = io_loop_context_new(ioloop);
+       io_loop_context_switch(ctx2);
+       struct event *ctx2_event1 = event_create(NULL);
+       event_push_global(ctx2_event1);
+       io_loop_context_deactivate(ctx2);
+
+       test_assert(event_get_global() == root_event);
+
+       /* test switching contexts */
+       io_loop_context_switch(ctx1);
+       test_assert(event_get_global() == ctx1_event2);
+       io_loop_context_switch(ctx2);
+       test_assert(event_get_global() == ctx2_event1);
+
+       /* test popping away events */
+       io_loop_context_switch(ctx1);
+       event_pop_global(ctx1_event2);
+       io_loop_context_switch(ctx2);
+       event_pop_global(ctx2_event1);
+       io_loop_context_switch(ctx1);
+       test_assert(event_get_global() == ctx1_event1);
+       io_loop_context_switch(ctx2);
+       test_assert(event_get_global() == root_event);
+
+       io_loop_context_deactivate(ctx2);
+       io_loop_context_unref(&ctx1);
+       io_loop_context_unref(&ctx2);
+       io_loop_destroy(&ioloop);
+
+       event_unref(&ctx1_event1);
+       event_unref(&ctx1_event2);
+       event_unref(&ctx2_event1);
+}
+
+static void test_ioloop_context_events(void)
+{
+       test_begin("ioloop context - no root event");
+       test_ioloop_context_events_run(NULL);
+       test_end();
+
+       test_begin("ioloop context - with root event");
+       struct event *root_event = event_create(NULL);
+       event_push_global(root_event);
+       test_ioloop_context_events_run(root_event);
+       event_pop_global(root_event);
+       event_unref(&root_event);
+       test_end();
+}
+
 void test_ioloop(void)
 {
        test_ioloop_timeout();
@@ -334,4 +400,5 @@ void test_ioloop(void)
        test_ioloop_pending_io();
        test_ioloop_fd();
        test_ioloop_context();
+       test_ioloop_context_events();
 }