Skip to content

Commit 5a5cb6b

Browse files
committed
sd-event: Add exit-on-idle support
Sometimes it's hard to assign responsibility to a specific event source for exiting when there's no more work to be done. So let's add exit-on-idle support where we exit when there are no more event sources.
1 parent c11e100 commit 5a5cb6b

File tree

5 files changed

+228
-0
lines changed

5 files changed

+228
-0
lines changed

man/sd_event_set_exit_on_idle.xml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?xml version='1.0'?>
2+
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
3+
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
4+
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
5+
6+
<refentry id="sd_event_set_exit_on_idle" xmlns:xi="http://www.w3.org/2001/XInclude">
7+
8+
<refentryinfo>
9+
<title>sd_event_set_exit_on_idle</title>
10+
<productname>systemd</productname>
11+
</refentryinfo>
12+
13+
<refmeta>
14+
<refentrytitle>sd_event_set_exit_on_idle</refentrytitle>
15+
<manvolnum>3</manvolnum>
16+
</refmeta>
17+
18+
<refnamediv>
19+
<refname>sd_event_set_exit_on_idle</refname>
20+
<refname>sd_event_get_exit_on_idle</refname>
21+
22+
<refpurpose>Enable event loop exit-on-idle support</refpurpose>
23+
</refnamediv>
24+
25+
<refsynopsisdiv>
26+
<funcsynopsis>
27+
<funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
28+
29+
<funcprototype>
30+
<funcdef>int <function>sd_event_set_exit_on_idle</function></funcdef>
31+
<paramdef>sd_event *<parameter>event</parameter></paramdef>
32+
<paramdef>int b</paramdef>
33+
</funcprototype>
34+
35+
<funcprototype>
36+
<funcdef>int <function>sd_event_get_exit_on_idle</function></funcdef>
37+
<paramdef>sd_event *<parameter>event</parameter></paramdef>
38+
</funcprototype>
39+
40+
</funcsynopsis>
41+
</refsynopsisdiv>
42+
43+
<refsect1>
44+
<title>Description</title>
45+
46+
<para><function>sd_event_set_exit_on_idle()</function> may be used to
47+
enable or disable the exit-on-idle support in the
48+
event loop object specified in the <parameter>event</parameter>
49+
parameter. If enabled, the event loop will exit with a zero exit code
50+
there are no more enabled (<constant>SD_EVENT_ON</constant>, <constant>SD_EVENT_ONESHOT</constant>),
51+
non-exit, non-post event sources.</para>
52+
53+
<para><function>sd_event_get_exit_on_idle()</function> may be used to
54+
determine whether exit-on-idle support was previously requested by a
55+
call to <function>sd_event_set_exit_on_idle()</function> with a true
56+
<parameter>b</parameter> parameter and successfully enabled.</para>
57+
</refsect1>
58+
59+
<refsect1>
60+
<title>Return Value</title>
61+
62+
<para>On success, <function>sd_event_set_exit_on_idle()</function> and
63+
<function>sd_event_get_exit_on_idle()</function> return a non-zero positive integer if the exit-on-idle
64+
support was successfully enabled. They return zero if the exit-on-idle support was explicitly disabled
65+
with a false <parameter>b</parameter> parameter. On failure, they return a negative errno-style error
66+
code.</para>
67+
68+
<refsect2>
69+
<title>Errors</title>
70+
71+
<para>Returned errors may indicate the following problems:</para>
72+
73+
<variablelist>
74+
75+
<varlistentry>
76+
<term><constant>-ECHILD</constant></term>
77+
78+
<listitem><para>The event loop has been created in a different process, library or module instance.</para></listitem>
79+
</varlistentry>
80+
81+
<varlistentry>
82+
<term><constant>-EINVAL</constant></term>
83+
84+
<listitem><para>The passed event loop object was invalid.</para></listitem>
85+
</varlistentry>
86+
87+
</variablelist>
88+
</refsect2>
89+
</refsect1>
90+
91+
<xi:include href="libsystemd-pkgconfig.xml" />
92+
93+
<refsect1>
94+
<title>History</title>
95+
<para><function>sd_event_set_exit_on_idle()</function> and
96+
<function>sd_event_get_exit_on_idle()</function> were added in version 259.</para>
97+
</refsect1>
98+
99+
<refsect1>
100+
<title>See Also</title>
101+
102+
<para><simplelist type="inline">
103+
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
104+
<member><citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
105+
<member><citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
106+
<member><citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
107+
<member><citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
108+
<member><citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
109+
<member><citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
110+
<member><citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
111+
<member><citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
112+
<member><citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
113+
</simplelist></para>
114+
</refsect1>
115+
116+
</refentry>

src/libsystemd/libsystemd.sym

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,5 +1081,7 @@ global:
10811081

10821082
LIBSYSTEMD_259 {
10831083
global:
1084+
sd_event_set_exit_on_idle;
1085+
sd_event_get_exit_on_idle;
10841086
sd_varlink_is_connected;
10851087
} LIBSYSTEMD_258;

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ struct sd_event {
157157
bool need_process_child:1;
158158
bool watchdog:1;
159159
bool profile_delays:1;
160+
bool exit_on_idle:1;
160161

161162
int exit_code;
162163

@@ -4422,6 +4423,28 @@ static int event_memory_pressure_write_list(sd_event *e) {
44224423
return 0;
44234424
}
44244425

4426+
static bool event_loop_idle(sd_event *e) {
4427+
assert(e);
4428+
4429+
LIST_FOREACH(sources, s, e->sources) {
4430+
/* Exit sources only trigger on exit, so whether they're enabled or not doesn't matter when
4431+
* we're deciding if the event loop is idle or not. */
4432+
if (s->type == SOURCE_EXIT)
4433+
continue;
4434+
4435+
if (s->enabled == SD_EVENT_OFF)
4436+
continue;
4437+
4438+
/* Post event sources always need another active event source to become pending. */
4439+
if (s->type == SOURCE_POST && !s->pending)
4440+
continue;
4441+
4442+
return false;
4443+
}
4444+
4445+
return true;
4446+
}
4447+
44254448
_public_ int sd_event_prepare(sd_event *e) {
44264449
int r;
44274450

@@ -4439,6 +4462,9 @@ _public_ int sd_event_prepare(sd_event *e) {
44394462
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
44404463
PROTECT_EVENT(e);
44414464

4465+
if (!e->exit_requested && e->exit_on_idle && event_loop_idle(e))
4466+
(void) sd_event_exit(e, 0);
4467+
44424468
if (e->exit_requested)
44434469
goto pending;
44444470

@@ -5243,6 +5269,22 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
52435269
return change;
52445270
}
52455271

5272+
_public_ int sd_event_set_exit_on_idle(sd_event *e, int b) {
5273+
assert_return(e, -EINVAL);
5274+
assert_return(e = event_resolve(e), -ENOPKG);
5275+
assert_return(!event_origin_changed(e), -ECHILD);
5276+
5277+
return e->exit_on_idle = b;
5278+
}
5279+
5280+
_public_ int sd_event_get_exit_on_idle(sd_event *e) {
5281+
assert_return(e, -EINVAL);
5282+
assert_return(e = event_resolve(e), -ENOPKG);
5283+
assert_return(!event_origin_changed(e), -ECHILD);
5284+
5285+
return e->exit_on_idle;
5286+
}
5287+
52465288
_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) {
52475289
_cleanup_free_ char *b = NULL;
52485290
_cleanup_free_ void *w = NULL;

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,4 +1065,70 @@ TEST(child_pidfd_wnowait) {
10651065
ASSERT_EQ(si.si_status, 42);
10661066
}
10671067

1068+
static int exit_on_idle_defer_handler(sd_event_source *s, void *userdata) {
1069+
unsigned *c = ASSERT_PTR(userdata);
1070+
1071+
/* Should not be reached on third call because the event loop should exit before */
1072+
ASSERT_LT(*c, 2u);
1073+
1074+
(*c)++;
1075+
1076+
/* Disable ourselves, which should trigger exit-on-idle after the second iteration */
1077+
if (*c == 2)
1078+
sd_event_source_set_enabled(s, SD_EVENT_OFF);
1079+
1080+
return 0;
1081+
}
1082+
1083+
static int exit_on_idle_post_handler(sd_event_source *s, void *userdata) {
1084+
unsigned *c = ASSERT_PTR(userdata);
1085+
1086+
/* Should not be reached on third call because the event loop should exit before */
1087+
ASSERT_LT(*c, 2u);
1088+
1089+
(*c)++;
1090+
return 0;
1091+
}
1092+
1093+
static int exit_on_idle_exit_handler(sd_event_source *s, void *userdata) {
1094+
return 0;
1095+
}
1096+
1097+
TEST(exit_on_idle) {
1098+
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
1099+
ASSERT_OK(sd_event_new(&e));
1100+
ASSERT_OK(sd_event_set_exit_on_idle(e, true));
1101+
ASSERT_OK_POSITIVE(sd_event_get_exit_on_idle(e));
1102+
1103+
/* Create a recurring defer event source. */
1104+
_cleanup_(sd_event_source_unrefp) sd_event_source *d = NULL;
1105+
unsigned dc = 0;
1106+
ASSERT_OK(sd_event_add_defer(e, &d, exit_on_idle_defer_handler, &dc));
1107+
ASSERT_OK(sd_event_source_set_enabled(d, SD_EVENT_ON));
1108+
1109+
/* This post event source should not keep the event loop running after the defer source is disabled. */
1110+
_cleanup_(sd_event_source_unrefp) sd_event_source *p = NULL;
1111+
unsigned pc = 0;
1112+
ASSERT_OK(sd_event_add_post(e, &p, exit_on_idle_post_handler, &pc));
1113+
ASSERT_OK(sd_event_source_set_enabled(p, SD_EVENT_ON));
1114+
ASSERT_OK(sd_event_source_set_priority(p, SD_EVENT_PRIORITY_IMPORTANT));
1115+
1116+
/* And neither should this exit event source. */
1117+
ASSERT_OK(sd_event_add_exit(e, NULL, exit_on_idle_exit_handler, NULL));
1118+
1119+
/* Run the event loop - it should exit after we disable the event source */
1120+
ASSERT_OK(sd_event_loop(e));
1121+
ASSERT_EQ(dc, 2u);
1122+
ASSERT_EQ(pc, 2u);
1123+
}
1124+
1125+
TEST(exit_on_idle_no_sources) {
1126+
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
1127+
ASSERT_OK(sd_event_new(&e));
1128+
ASSERT_OK(sd_event_set_exit_on_idle(e, true));
1129+
1130+
/* Running loop with no sources should return immediately with success */
1131+
ASSERT_OK(sd_event_loop(e));
1132+
}
1133+
10681134
DEFINE_TEST_MAIN(LOG_DEBUG);

src/systemd/sd-event.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ int sd_event_set_watchdog(sd_event *e, int b);
116116
int sd_event_get_watchdog(sd_event *e);
117117
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
118118
int sd_event_set_signal_exit(sd_event *e, int b);
119+
int sd_event_set_exit_on_idle(sd_event *e, int b);
120+
int sd_event_get_exit_on_idle(sd_event *e);
119121

120122
sd_event_source* sd_event_source_ref(sd_event_source *s);
121123
sd_event_source* sd_event_source_unref(sd_event_source *s);

0 commit comments

Comments
 (0)