From: Timo Sirainen Date: Thu, 11 Mar 2021 00:19:37 +0000 (+0200) Subject: lib: Push/pop global event stack automatically when ioloop contexts are switched X-Git-Tag: 2.3.18~364 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c633d08bf2d223dfe63bd148d7a3435f9fa0f1a1;p=thirdparty%2Fdovecot%2Fcore.git lib: Push/pop global event stack automatically when ioloop contexts are switched --- diff --git a/src/lib/ioloop-private.h b/src/lib/ioloop-private.h index abf2a44a3f..b7b2aafece 100644 --- a/src/lib/ioloop-private.h +++ b/src/lib/ioloop-private.h @@ -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); diff --git a/src/lib/ioloop.c b/src/lib/ioloop.c index db68b7e454..c1db61a2f2 100644 --- a/src/lib/ioloop.c +++ b/src/lib/ioloop.c @@ -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); } diff --git a/src/lib/lib-event.h b/src/lib/lib-event.h index 7d1b6da030..c7ea4ef13d 100644 --- a/src/lib/lib-event.h +++ b/src/lib/lib-event.h @@ -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); diff --git a/src/lib/test-ioloop.c b/src/lib/test-ioloop.c index b0ab89387c..89f688872c 100644 --- a/src/lib/test-ioloop.c +++ b/src/lib/test-ioloop.c @@ -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(); }