mirror of
https://github.com/systemd/systemd.git
synced 2024-11-26 19:53:45 +08:00
timer: introduce DeferReactivation setting
By default, in instances where timers are running on a realtime schedule, if a service takes longer to run than the interval of a timer, the service will immediately start again when the previous invocation finishes. This is caused by the fact that the next elapse is calculated based on the last trigger time, which, combined with the fact that the interval is shorter than the runtime of the service, causes that elapse to be in the past, which in turn means the timer will trigger as soon as the service finishes running. This behavior can be changed by enabling the new DeferReactivation setting, which will cause the next calendar elapse to be calculated based on when the trigger unit enters inactivity, rather than the last trigger time. Thus, if a timer is on an realtime interval, the trigger will always adhere to that specified interval. E.g. if you have a timer that runs on a minutely interval, the setting guarantees that triggers will happen at *:*:00 times, whereas by default this may skew depending on how long the service runs. Co-authored-by: Matteo Croce <teknoraver@meta.com>
This commit is contained in:
parent
632407c3e4
commit
cc0ab8c810
@ -387,6 +387,7 @@ Most timer unit settings are available to transient units.
|
||||
✓ AccuracySec=
|
||||
✓ RandomizedDelaySec=
|
||||
✓ FixedRandomDelay=
|
||||
✓ DeferReactivation=
|
||||
Unit=
|
||||
```
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -61,6 +61,7 @@ struct Timer {
|
||||
bool on_clock_change;
|
||||
bool on_timezone_change;
|
||||
bool fixed_random_delay;
|
||||
bool defer_reactivation;
|
||||
|
||||
char *stamp_path;
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -7,6 +7,7 @@ AllowedCPUs=
|
||||
AllowedMemoryNodes=
|
||||
AllowIsolate=
|
||||
Also=
|
||||
DeferReactivation=
|
||||
AmbientCapabilities=
|
||||
AssertACPower=
|
||||
AssertArchitecture=
|
||||
|
@ -33,6 +33,7 @@ Persistent=true
|
||||
AccuracySec=24h
|
||||
RandomizedDelaySec=234234234
|
||||
FixedRandomDelay=true
|
||||
DeferReactivation=true
|
||||
|
||||
Persistent=no
|
||||
Unit=foo.service
|
||||
|
Loading…
Reference in New Issue
Block a user