sd-event: Make sure iterations of defer and exit sources are updated

Defer and exit event sources are marked pending once when they are added
and never again afterwards. This means their pending_iteration is never
incremented after they are initially added, which breaks fairness among
event sources with equal priority which depend on the pending_iteration
variable getting updated in source_set_pending(). To fix this, let's assign
iterations for defer and exit sources in source_dispatch() instead so that
those get their pending_iteration updated as well.
This commit is contained in:
Daan De Meyer
2025-11-13 22:15:01 +01:00
parent 5a5cb6ba50
commit a3dd54c097
2 changed files with 43 additions and 1 deletions

View File

@@ -4120,7 +4120,13 @@ static int source_dispatch(sd_event_source *s) {
return 1;
}
if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
if (IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
/* Make sure this event source is moved to the end of the priority list now. We do this here
* because defer and exit event sources are always pending from the moment they're added so
* the same logic in source_set_pending() is never triggered. */
s->pending_iteration = s->event->iteration;
event_source_pp_prioq_reshuffle(s);
} else {
r = source_set_pending(s, false);
if (r < 0)
return r;

View File

@@ -1131,4 +1131,40 @@ TEST(exit_on_idle_no_sources) {
ASSERT_OK(sd_event_loop(e));
}
static int defer_fair_handler(sd_event_source *s, void *userdata) {
unsigned *counter = ASSERT_PTR(userdata);
/* If we're about to increment above 5, exit the event loop */
if (*counter >= 5)
return sd_event_exit(sd_event_source_get_event(s), 0);
(*counter)++;
return 0;
}
TEST(defer_fair_scheduling) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
sd_event_source *sources[5] = {};
unsigned counters[5] = {};
ASSERT_OK(sd_event_new(&e));
ASSERT_OK(sd_event_set_exit_on_idle(e, true));
/* Create 5 defer sources with equal priority */
for (unsigned i = 0; i < 5; i++) {
ASSERT_OK(sd_event_add_defer(e, &sources[i], defer_fair_handler, &counters[i]));
ASSERT_OK(sd_event_source_set_enabled(sources[i], SD_EVENT_ON));
}
/* Run the event loop until one of the handlers exits */
ASSERT_OK(sd_event_loop(e));
/* All counters should be equal to 5, demonstrating fair scheduling */
for (unsigned i = 0; i < 5; i++) {
ASSERT_EQ(counters[i], 5u);
sd_event_source_unref(sources[i]);
}
}
DEFINE_TEST_MAIN(LOG_DEBUG);