--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sd_event_set_exit_on_idle" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_event_set_exit_on_idle</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_event_set_exit_on_idle</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_event_set_exit_on_idle</refname>
+ <refname>sd_event_get_exit_on_idle</refname>
+
+ <refpurpose>Enable event loop exit-on-idle support</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-event.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_event_set_exit_on_idle</function></funcdef>
+ <paramdef>sd_event *<parameter>event</parameter></paramdef>
+ <paramdef>int b</paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_event_get_exit_on_idle</function></funcdef>
+ <paramdef>sd_event *<parameter>event</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_event_set_exit_on_idle()</function> may be used to
+ enable or disable the exit-on-idle support in the
+ event loop object specified in the <parameter>event</parameter>
+ parameter. If enabled, the event loop will exit with a zero exit code
+ there are no more enabled (<constant>SD_EVENT_ON</constant>, <constant>SD_EVENT_ONESHOT</constant>),
+ non-exit, non-post event sources.</para>
+
+ <para><function>sd_event_get_exit_on_idle()</function> may be used to
+ determine whether exit-on-idle support was previously requested by a
+ call to <function>sd_event_set_exit_on_idle()</function> with a true
+ <parameter>b</parameter> parameter and successfully enabled.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, <function>sd_event_set_exit_on_idle()</function> and
+ <function>sd_event_get_exit_on_idle()</function> return a non-zero positive integer if the exit-on-idle
+ support was successfully enabled. They return zero if the exit-on-idle support was explicitly disabled
+ with a false <parameter>b</parameter> parameter. On failure, they return a negative errno-style error
+ code.</para>
+
+ <refsect2>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><constant>-ECHILD</constant></term>
+
+ <listitem><para>The event loop has been created in a different process, library or module instance.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The passed event loop object was invalid.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>History</title>
+ <para><function>sd_event_set_exit_on_idle()</function> and
+ <function>sd_event_get_exit_on_idle()</function> were added in version 259.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+
+</refentry>
LIBSYSTEMD_259 {
global:
+ sd_event_set_exit_on_idle;
+ sd_event_get_exit_on_idle;
sd_varlink_is_connected;
} LIBSYSTEMD_258;
bool need_process_child:1;
bool watchdog:1;
bool profile_delays:1;
+ bool exit_on_idle:1;
int exit_code;
return 0;
}
+static bool event_loop_idle(sd_event *e) {
+ assert(e);
+
+ LIST_FOREACH(sources, s, e->sources) {
+ /* Exit sources only trigger on exit, so whether they're enabled or not doesn't matter when
+ * we're deciding if the event loop is idle or not. */
+ if (s->type == SOURCE_EXIT)
+ continue;
+
+ if (s->enabled == SD_EVENT_OFF)
+ continue;
+
+ /* Post event sources always need another active event source to become pending. */
+ if (s->type == SOURCE_POST && !s->pending)
+ continue;
+
+ return false;
+ }
+
+ return true;
+}
+
_public_ int sd_event_prepare(sd_event *e) {
int r;
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
PROTECT_EVENT(e);
+ if (!e->exit_requested && e->exit_on_idle && event_loop_idle(e))
+ (void) sd_event_exit(e, 0);
+
if (e->exit_requested)
goto pending;
return change;
}
+_public_ int sd_event_set_exit_on_idle(sd_event *e, int b) {
+ assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
+ assert_return(!event_origin_changed(e), -ECHILD);
+
+ return e->exit_on_idle = b;
+}
+
+_public_ int sd_event_get_exit_on_idle(sd_event *e) {
+ assert_return(e, -EINVAL);
+ assert_return(e = event_resolve(e), -ENOPKG);
+ assert_return(!event_origin_changed(e), -ECHILD);
+
+ return e->exit_on_idle;
+}
+
_public_ int sd_event_source_set_memory_pressure_type(sd_event_source *s, const char *ty) {
_cleanup_free_ char *b = NULL;
_cleanup_free_ void *w = NULL;
ASSERT_EQ(si.si_status, 42);
}
+static int exit_on_idle_defer_handler(sd_event_source *s, void *userdata) {
+ unsigned *c = ASSERT_PTR(userdata);
+
+ /* Should not be reached on third call because the event loop should exit before */
+ ASSERT_LT(*c, 2u);
+
+ (*c)++;
+
+ /* Disable ourselves, which should trigger exit-on-idle after the second iteration */
+ if (*c == 2)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
+
+ return 0;
+}
+
+static int exit_on_idle_post_handler(sd_event_source *s, void *userdata) {
+ unsigned *c = ASSERT_PTR(userdata);
+
+ /* Should not be reached on third call because the event loop should exit before */
+ ASSERT_LT(*c, 2u);
+
+ (*c)++;
+ return 0;
+}
+
+static int exit_on_idle_exit_handler(sd_event_source *s, void *userdata) {
+ return 0;
+}
+
+TEST(exit_on_idle) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ ASSERT_OK(sd_event_new(&e));
+ ASSERT_OK(sd_event_set_exit_on_idle(e, true));
+ ASSERT_OK_POSITIVE(sd_event_get_exit_on_idle(e));
+
+ /* Create a recurring defer event source. */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *d = NULL;
+ unsigned dc = 0;
+ ASSERT_OK(sd_event_add_defer(e, &d, exit_on_idle_defer_handler, &dc));
+ ASSERT_OK(sd_event_source_set_enabled(d, SD_EVENT_ON));
+
+ /* This post event source should not keep the event loop running after the defer source is disabled. */
+ _cleanup_(sd_event_source_unrefp) sd_event_source *p = NULL;
+ unsigned pc = 0;
+ ASSERT_OK(sd_event_add_post(e, &p, exit_on_idle_post_handler, &pc));
+ ASSERT_OK(sd_event_source_set_enabled(p, SD_EVENT_ON));
+ ASSERT_OK(sd_event_source_set_priority(p, SD_EVENT_PRIORITY_IMPORTANT));
+
+ /* And neither should this exit event source. */
+ ASSERT_OK(sd_event_add_exit(e, NULL, exit_on_idle_exit_handler, NULL));
+
+ /* Run the event loop - it should exit after we disable the event source */
+ ASSERT_OK(sd_event_loop(e));
+ ASSERT_EQ(dc, 2u);
+ ASSERT_EQ(pc, 2u);
+}
+
+TEST(exit_on_idle_no_sources) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ ASSERT_OK(sd_event_new(&e));
+ ASSERT_OK(sd_event_set_exit_on_idle(e, true));
+
+ /* Running loop with no sources should return immediately with success */
+ ASSERT_OK(sd_event_loop(e));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
int sd_event_get_watchdog(sd_event *e);
int sd_event_get_iteration(sd_event *e, uint64_t *ret);
int sd_event_set_signal_exit(sd_event *e, int b);
+int sd_event_set_exit_on_idle(sd_event *e, int b);
+int sd_event_get_exit_on_idle(sd_event *e);
sd_event_source* sd_event_source_ref(sd_event_source *s);
sd_event_source* sd_event_source_unref(sd_event_source *s);