]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-event: Mark post sources as pending after dispatching
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 6 Nov 2025 09:20:49 +0000 (10:20 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 14 Nov 2025 09:34:30 +0000 (10:34 +0100)
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.

src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c

index d8cd1ba7dfed1c61733583677cd22db2ae49c463..120e67cc31842385e338a7bd923c2b2d717da244 100644 (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",
index 77cf7af541afabae07b92b0f171a519abdede465..31931aea193e8c06cdbfef701cbc328b7395dde0 100644 (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);