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);
i_assert(ctx->ioloop->cur_ctx != ctx);
array_free(&ctx->callbacks);
+ array_free(&ctx->global_event_stack);
i_free(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;
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);
}
}
ctx->ioloop->cur_ctx = NULL;
+ io_loop_context_pop_global_events(ctx);
io_loop_context_remove_deleted_callbacks(ctx);
io_loop_context_unref(&ctx);
}
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);
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();
test_ioloop_pending_io();
test_ioloop_fd();
test_ioloop_context();
+ test_ioloop_context_events();
}