sd-event: Mark post sources as pending after dispatching

More post event sources might get added during dispatching, we want
to make sure those become pending as well if we're dispatching a non-post
event source.
This commit is contained in:
Daan De Meyer
2025-11-06 10:20:49 +01:00
parent 6403a81b28
commit 4c8b6d636c
2 changed files with 66 additions and 11 deletions

View File

@@ -4083,6 +4083,22 @@ static int source_memory_pressure_initiate_dispatch(sd_event_source *s) {
return 0; /* go on, dispatch to user callback */
}
static int mark_post_sources_pending(sd_event *e) {
sd_event_source *z;
int r;
SET_FOREACH(z, e->post_sources) {
if (event_source_is_offline(z))
continue;
r = source_set_pending(z, true);
if (r < 0)
return r;
}
return 0;
}
static int source_dispatch(sd_event_source *s) {
EventSourceType saved_type;
sd_event *saved_event;
@@ -4117,18 +4133,10 @@ static int source_dispatch(sd_event_source *s) {
}
if (s->type != SOURCE_POST) {
sd_event_source *z;
/* If we execute a non-post source, let's mark all post sources as pending. */
SET_FOREACH(z, s->event->post_sources) {
if (event_source_is_offline(z))
continue;
r = source_set_pending(z, true);
if (r < 0)
return r;
}
r = mark_post_sources_pending(s->event);
if (r < 0)
return r;
}
if (s->type == SOURCE_MEMORY_PRESSURE) {
@@ -4237,6 +4245,14 @@ static int source_dispatch(sd_event_source *s) {
s->dispatching = false;
if (saved_type != SOURCE_POST) {
/* More post sources might have been added while executing the callback, let's make sure
* those are marked pending as well. */
r = mark_post_sources_pending(saved_event);
if (r < 0)
return r;
}
finish:
if (r < 0) {
log_debug_errno(r, "Event source %s (type %s) returned error, %s: %m",

View File

@@ -946,4 +946,43 @@ TEST(leave_ratelimit) {
ASSERT_TRUE(manually_left_ratelimit);
}
static int defer_post_handler(sd_event_source *s, void *userdata) {
bool *dispatched_post = ASSERT_PTR(userdata);
*dispatched_post = true;
return 0;
}
static int defer_adds_post_handler(sd_event_source *s, void *userdata) {
sd_event *e = sd_event_source_get_event(s);
/* Add a post event source from within the defer handler */
ASSERT_OK(sd_event_add_post(e, NULL, defer_post_handler, userdata));
return 0;
}
TEST(defer_add_post) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
bool dispatched_post = false;
ASSERT_OK(sd_event_default(&e));
/* Add a oneshot defer event source that will add a post event source */
ASSERT_OK(sd_event_add_defer(e, NULL, defer_adds_post_handler, &dispatched_post));
/* Run one iteration - this should dispatch the defer handler */
ASSERT_OK_POSITIVE(sd_event_run(e, UINT64_MAX));
/* The post handler should have been added but not yet dispatched */
ASSERT_FALSE(dispatched_post);
/* Run another iteration - this should dispatch the post handler */
ASSERT_OK_POSITIVE(sd_event_run(e, 0));
/* Now the post handler should have been dispatched */
ASSERT_TRUE(dispatched_post);
}
DEFINE_TEST_MAIN(LOG_DEBUG);