1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 17:27:41 +03:00

Merge pull request #34591 from teknoraver/timer

timer: introduce DeferReactivation setting
This commit is contained in:
Yu Watanabe 2024-10-12 08:12:38 +09:00 committed by GitHub
commit 39b2ee5369
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 97 additions and 8 deletions

View File

@ -387,6 +387,7 @@ Most timer unit settings are available to transient units.
✓ AccuracySec=
✓ RandomizedDelaySec=
✓ FixedRandomDelay=
✓ DeferReactivation=
Unit=
```

View File

@ -8804,6 +8804,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
readonly b WakeSystem = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RemainAfterElapse = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DeferReactivation = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
@ -8832,6 +8834,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
<!--property RemainAfterElapse is not documented!-->
<!--property DeferReactivation is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
@ -8874,6 +8878,8 @@ node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
<variablelist class="dbus-property" generated="True" extra-ref="RemainAfterElapse"/>
<variablelist class="dbus-property" generated="True" extra-ref="DeferReactivation"/>
<!--End of Autogenerated section-->
<refsect2>
@ -12386,5 +12392,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<title>Job Objects</title>
<para><varname>ActivationDetails</varname> was added in version 252.</para>
</refsect2>
<refsect2>
<title>Timer Objects</title>
<para><varname>DeferReactivation</varname> was added in version 257.</para>
</refsect2>
</refsect1>
</refentry>

View File

@ -305,6 +305,25 @@
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>DeferReactivation=</varname></term>
<listitem><para>Takes a boolean argument. When enabled, the timer schedules the next elapse based on
the trigger unit entering inactivity, instead of the last trigger time.
This is most apparent in the case where the service unit takes longer to run than the timer interval.
With this setting enabled, the timer will schedule the next elapse based on when the service finishes
running, and so it will have to wait until the next realtime elapse time to trigger.
Otherwise, the default behavior is for the timer unit to immediately trigger again once the service
finishes running. This happens because the timer schedules the next elapse based on the previous trigger
time, and since the interval is shorter than the service runtime, that elapse will be in the past,
causing it to immediately trigger once done.</para>
<para>This setting has no effect if a realtime timer has not been specified with
<varname>OnCalendar=</varname>. Defaults to <option>false</option>.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>OnClockChange=</varname></term>
<term><varname>OnTimezoneChange=</varname></term>

View File

@ -118,6 +118,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DeferReactivation", "b", bus_property_get_bool, offsetof(Timer, defer_reactivation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@ -233,6 +234,9 @@ static int bus_timer_set_transient_property(
if (streq(name, "OnClockChange"))
return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error);
if (streq(name, "DeferReactivation"))
return bus_set_transient_bool(u, name, &t->defer_reactivation, message, flags, error);
if (streq(name, "TimersMonotonic")) {
const char *base_name;
usec_t usec;

View File

@ -570,6 +570,7 @@ Timer.Persistent, config_parse_bool,
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.FixedRandomDelay, config_parse_bool, 0, offsetof(Timer, fixed_random_delay)
Timer.DeferReactivation, config_parse_bool, 0, offsetof(Timer, defer_reactivation)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0

View File

@ -245,7 +245,8 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%sRemainAfterElapse: %s\n"
"%sFixedRandomDelay: %s\n"
"%sOnClockChange: %s\n"
"%sOnTimeZoneChange: %s\n",
"%sOnTimeZoneChange: %s\n"
"%sDeferReactivation: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
prefix, trigger ? trigger->id : "n/a",
@ -255,7 +256,8 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(t->remain_after_elapse),
prefix, yes_no(t->fixed_random_delay),
prefix, yes_no(t->on_clock_change),
prefix, yes_no(t->on_timezone_change));
prefix, yes_no(t->on_timezone_change),
prefix, yes_no(t->defer_reactivation));
LIST_FOREACH(value, v, t->values)
if (v->base == TIMER_CALENDAR) {
@ -391,12 +393,19 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
if (v->base == TIMER_CALENDAR) {
usec_t b, rebased;
/* If we know the last time this was
* triggered, schedule the job based relative
* to that. If we don't, just start from
* the activation time. */
/* If DeferReactivation= is enabled, schedule the job based on the last time
* the trigger unit entered inactivity. Otherwise, if we know the last time
* this was triggered, schedule the job based relative to that. If we don't,
* just start from the activation time or realtime. */
if (dual_timestamp_is_set(&t->last_trigger))
if (t->defer_reactivation &&
dual_timestamp_is_set(&trigger->inactive_enter_timestamp)) {
if (dual_timestamp_is_set(&t->last_trigger))
b = MAX(trigger->inactive_enter_timestamp.realtime,
t->last_trigger.realtime);
else
b = trigger->inactive_enter_timestamp.realtime;
} else if (dual_timestamp_is_set(&t->last_trigger))
b = t->last_trigger.realtime;
else if (dual_timestamp_is_set(&UNIT(t)->inactive_exit_timestamp))
b = UNIT(t)->inactive_exit_timestamp.realtime;

View File

@ -61,6 +61,7 @@ struct Timer {
bool on_clock_change;
bool on_timezone_change;
bool fixed_random_delay;
bool defer_reactivation;
char *stamp_path;
};

View File

@ -2594,7 +2594,8 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
"Persistent",
"OnTimezoneChange",
"OnClockChange",
"FixedRandomDelay"))
"FixedRandomDelay",
"DeferReactivation"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "AccuracySec",

View File

@ -0,0 +1,6 @@
[Unit]
Description=Testing systemd timers
[Service]
Type=simple
ExecStart=sh -c 'date +%%s >>/tmp/realtime-test.log ; sleep 5'

View File

@ -0,0 +1,10 @@
[Unit]
Description=Testing systemd timers
[Timer]
OnCalendar=*:*:0/5
AccuracySec=1us
DeferReactivation=true
[Install]
WantedBy=timers.target

View File

@ -7,3 +7,5 @@ integration_tests += [
'vm' : true,
},
]
testdata_subdirs += [meson.current_source_dir() / 'TEST-74-AUX-UTILS.units']

View File

@ -7,6 +7,7 @@ AllowedCPUs=
AllowedMemoryNodes=
AllowIsolate=
Also=
DeferReactivation=
AmbientCapabilities=
AssertACPower=
AssertArchitecture=

View File

@ -33,6 +33,7 @@ Persistent=true
AccuracySec=24h
RandomizedDelaySec=234234234
FixedRandomDelay=true
DeferReactivation=true
Persistent=no
Unit=foo.service

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
systemctl start realtime-test.timer
sleep 35
mindelta=10
last=
while read -r time; do
if [ -n "$last" ]; then
delta=$((time - last))
if [ "$delta" -lt $mindelta ]; then
echo "Timer fired too early: $delta < $mindelta" >/failed
break
fi
fi
last=$time
done </tmp/realtime-test.log
test ! -s /failed