[systemd-devel] [PATCH] Implement OnTransactionFinishedSec= for timers

Christian Seiler christian at iwakd.de
Thu Sep 27 02:25:18 PDT 2012


Add OnTransactionFinishedSec= setting for timers that allows for timers to
fire at a specific interval after the transaction in which they were started
has completed.

This is specifically useful for running services after boot. For example,
having a timer with OnTransactionFinishedSec=0 and enabling it in
default.target will case the timer to start as soon as the system has
reached default.target, but not before.

Signed-off-by: Christian Seiler <christian at iwakd.de>
---
 man/systemd.timer.xml                 | 17 +++++++++++--
 src/core/load-fragment-gperf.gperf.m4 |  1 +
 src/core/manager.c                    | 21 +++++++++++++++
 src/core/manager.h                    |  5 ++++
 src/core/timer.c                      | 48 ++++++++++++++++++++++++++++++-----
 src/core/timer.h                      |  9 +++++++
 6 files changed, 92 insertions(+), 9 deletions(-)

diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 6fc26a5..c20e21a 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -104,6 +104,7 @@
                                 <term><varname>OnStartupSec=</varname></term>
                                 <term><varname>OnUnitActiveSec=</varname></term>
                                 <term><varname>OnUnitInactiveSec=</varname></term>
+                                <term><varname>OnTransactionFinishedSec=</varname></term>
 
                                 <listitem><para>Defines timers
                                 relative to different starting points:
@@ -122,7 +123,10 @@
                                 activated. <varname>OnUnitInactiveSec=</varname>
                                 defines a timer relative to when the
                                 unit the timer is activating was last
-                                deactivated.</para>
+                                deactivated. <varname>OnTransactionFinishedSec=</varname>
+                                defines a timer relative to the end
+                                of the transaction in which the timer
+                                was started.</para>
 
                                 <para>Multiple directives may be
                                 combined of the same and of different
@@ -152,7 +156,16 @@
                                 elapse and the configured unit is
                                 started. This is not the case for
                                 timers defined in the other
-                                directives.</para></listitem>
+                                directives.</para>
+
+                                <para>The <varname>OnTransactionFinishedSec=</varname>
+                                setting is useful for starting a timer
+                                after the system finished booting (in
+                                contrast to <varname>OnBootSec=</varname>,
+                                which defines timers relative to the
+                                point the system started booting) if the
+                                timer is enabled in
+                                <filename>default.target</filename>.</para></listitem>
 
                                 <para>These are monotonic timers,
                                 independent of wall-clock time and timezones. If the
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 8187cd4..f68fa12 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -235,6 +235,7 @@ Timer.OnBootSec,                 config_parse_timer,                 0,
 Timer.OnStartupSec,              config_parse_timer,                 0,                             0
 Timer.OnUnitActiveSec,           config_parse_timer,                 0,                             0
 Timer.OnUnitInactiveSec,         config_parse_timer,                 0,                             0
+Timer.OnTransactionFinishedSec,  config_parse_timer,                 0,                             0
 Timer.Unit,                      config_parse_timer_unit,            0,                             0
 m4_dnl
 Path.PathExists,                 config_parse_path_spec,             0,                             0
diff --git a/src/core/manager.c b/src/core/manager.c
index 3cd9915..78e3856 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -461,6 +461,7 @@ static void manager_clear_jobs_and_units(Manager *m) {
         assert(!m->dbus_job_queue);
         assert(!m->cleanup_queue);
         assert(!m->gc_queue);
+        assert(!m->transaction_timer_queue);
 
         assert(hashmap_isempty(m->jobs));
         assert(hashmap_isempty(m->units));
@@ -944,6 +945,23 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
         return n;
 }
 
+unsigned manager_dispatch_transaction_timer_queue(Manager *m) {
+        Timer *t;
+        unsigned n = 0;
+        usec_t finish_time;
+
+        finish_time = now(CLOCK_MONOTONIC);
+
+        while ((t = m->transaction_timer_queue)) {
+                assert(t->in_transaction_timer_queue);
+
+                timer_notify_transaction_finished(t, finish_time);
+                n++;
+        }
+
+        return n;
+}
+
 static int manager_process_notify_fd(Manager *m) {
         ssize_t n;
 
@@ -2027,6 +2045,9 @@ void manager_check_finished(Manager *m) {
         /* Notify Type=idle units that we are done now */
         close_pipe(m->idle_pipe);
 
+        /* Recalculate all timers that are based on end-of-transaction times */
+        manager_dispatch_transaction_timer_queue(m);
+
         /* Turn off confirm spawn now */
         m->confirm_spawn = false;
 
diff --git a/src/core/manager.h b/src/core/manager.h
index 913752f..d9d4785 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -34,6 +34,7 @@
 typedef struct Manager Manager;
 typedef enum WatchType WatchType;
 typedef struct Watch Watch;
+typedef struct Timer Timer;
 
 typedef enum ManagerExitCode {
         MANAGER_RUNNING,
@@ -113,6 +114,9 @@ struct Manager {
         LIST_HEAD(Unit, dbus_unit_queue);
         LIST_HEAD(Job, dbus_job_queue);
 
+        /* Timers to ping after transaction is done */
+        LIST_HEAD(Timer, transaction_timer_queue);
+
         /* Units to remove */
         LIST_HEAD(Unit, cleanup_queue);
 
@@ -262,6 +266,7 @@ void manager_clear_jobs(Manager *m);
 unsigned manager_dispatch_load_queue(Manager *m);
 unsigned manager_dispatch_run_queue(Manager *m);
 unsigned manager_dispatch_dbus_queue(Manager *m);
+unsigned manager_dispatch_transaction_timer_queue(Manager *m);
 
 int manager_set_default_controllers(Manager *m, char **controllers);
 int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
diff --git a/src/core/timer.c b/src/core/timer.c
index 7080b32..16749ba 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -43,6 +43,7 @@ static void timer_init(Unit *u) {
         assert(u->load_state == UNIT_STUB);
 
         t->next_elapse = (usec_t) -1;
+        t->last_transaction_finished = (usec_t) -1;
 }
 
 static void timer_done(Unit *u) {
@@ -58,6 +59,9 @@ static void timer_done(Unit *u) {
 
         unit_unwatch_timer(u, &t->timer_watch);
 
+        if (t->in_transaction_timer_queue)
+                LIST_REMOVE(Timer, transaction_timer_queue, u->manager->transaction_timer_queue, t);
+
         unit_ref_unset(&t->unit);
 }
 
@@ -166,7 +170,7 @@ static void timer_set_state(Timer *t, TimerState state) {
         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
 }
 
-static void timer_enter_waiting(Timer *t, bool initial);
+static void timer_enter_waiting(Timer *t, bool initial, bool transaction_just_finished);
 
 static int timer_coldplug(Unit *u) {
         Timer *t = TIMER(u);
@@ -177,7 +181,7 @@ static int timer_coldplug(Unit *u) {
         if (t->deserialized_state != t->state) {
 
                 if (t->deserialized_state == TIMER_WAITING)
-                        timer_enter_waiting(t, false);
+                        timer_enter_waiting(t, false, false);
                 else
                         timer_set_state(t, t->deserialized_state);
         }
@@ -194,7 +198,7 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
 }
 
-static void timer_enter_waiting(Timer *t, bool initial) {
+static void timer_enter_waiting(Timer *t, bool initial, bool transaction_just_finished) {
         TimerValue *v;
         usec_t base = 0, delay, n;
         bool found = false;
@@ -241,6 +245,24 @@ static void timer_enter_waiting(Timer *t, bool initial) {
                         base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
                         break;
 
+                case TIMER_TRANSACTION_FINISHED:
+
+                        if (t->last_transaction_finished == (usec_t) -1) {
+                                if (initial) {
+                                        LIST_PREPEND(Timer, transaction_timer_queue, UNIT(t)->manager->transaction_timer_queue, t);
+                                        t->in_transaction_timer_queue = true;
+                                }
+                                continue;
+                        }
+
+                        base = t->last_transaction_finished;
+
+                        /* if we are called the first time after a transaction just finished,
+                         * ensure that the event will be considered (initial = false!) */
+                        if (transaction_just_finished && base + v->value < n)
+                                base = n - v->value;
+                        break;
+
                 default:
                         assert_not_reached("Unknown timer base");
                 }
@@ -312,7 +334,8 @@ static int timer_start(Unit *u) {
                 return -ENOENT;
 
         t->result = TIMER_SUCCESS;
-        timer_enter_waiting(t, true);
+        t->last_transaction_finished = (usec_t) -1;
+        timer_enter_waiting(t, true, false);
         return 0;
 }
 
@@ -425,14 +448,14 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) {
                 case TIMER_ELAPSED:
 
                         /* Recalculate sleep time */
-                        timer_enter_waiting(t, false);
+                        timer_enter_waiting(t, false, false);
                         break;
 
                 case TIMER_RUNNING:
 
                         if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
                                 log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
-                                timer_enter_waiting(t, false);
+                                timer_enter_waiting(t, false, false);
                         }
 
                         break;
@@ -447,6 +470,16 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) {
         }
 }
 
+void timer_notify_transaction_finished(Timer *t, usec_t finish_time)
+{
+        t->last_transaction_finished = finish_time;
+        if (t->in_transaction_timer_queue) {
+                LIST_REMOVE(Timer, transaction_timer_queue, UNIT(t)->manager->transaction_timer_queue, t);
+                t->in_transaction_timer_queue = false;
+        }
+        timer_enter_waiting(t, false, true);
+}
+
 static void timer_reset_failed(Unit *u) {
         Timer *t = TIMER(u);
 
@@ -473,7 +506,8 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
         [TIMER_BOOT] = "OnBootSec",
         [TIMER_STARTUP] = "OnStartupSec",
         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
-        [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
+        [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
+        [TIMER_TRANSACTION_FINISHED] = "OnTransactionFinishedSec"
 };
 
 DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
diff --git a/src/core/timer.h b/src/core/timer.h
index c6d1d42..94a4213 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -41,6 +41,7 @@ typedef enum TimerBase {
         TIMER_STARTUP,
         TIMER_UNIT_ACTIVE,
         TIMER_UNIT_INACTIVE,
+        TIMER_TRANSACTION_FINISHED,
         _TIMER_BASE_MAX,
         _TIMER_BASE_INVALID = -1
 } TimerBase;
@@ -66,6 +67,7 @@ struct Timer {
         Unit meta;
 
         LIST_HEAD(TimerValue, values);
+        usec_t last_transaction_finished;
         usec_t next_elapse;
 
         TimerState state, deserialized_state;
@@ -74,10 +76,17 @@ struct Timer {
         Watch timer_watch;
 
         TimerResult result;
+
+        /* Transaction timer queue */
+        LIST_FIELDS(Timer, transaction_timer_queue);
+
+        bool in_transaction_timer_queue:1;
 };
 
 void timer_unit_notify(Unit *u, UnitActiveState new_state);
 
+void timer_notify_transaction_finished(Timer *t, usec_t finish_time);
+
 extern const UnitVTable timer_vtable;
 
 const char *timer_state_to_string(TimerState i);
-- 
1.7.11.4



More information about the systemd-devel mailing list