j->installed = false;
}
-static bool job_type_allows_late_merge(JobType t) {
- /* Tells whether it is OK to merge a job of type 't' with an already
- * running job.
- * Reloads cannot be merged this way. Think of the sequence:
- * 1. Reload of a daemon is in progress; the daemon has already loaded
- * its config file, but hasn't completed the reload operation yet.
+static bool jobs_may_late_merge(const Job *j, const Job *uj) {
+ assert(j);
+ assert(!j->installed);
+ assert(uj);
+ assert(uj->installed);
+ assert(uj->state == JOB_RUNNING);
+
+ /* Tells whether it is OK to merge a job with an already running job. */
+
+ if (j->refuse_late_merge) /* refused when constructing transaction? */
+ return false;
+
+ /* Reloads cannot be merged this way. Think of the sequence:
+ * 1. Reload of a daemon is in progress; the daemon has already loaded its config file, but hasn't
+ * completed the reload operation yet.
* 2. Edit foo's config file.
* 3. Trigger another reload to have the daemon use the new config.
- * Should the second reload job be merged into the first one, the daemon
- * would not know about the new config.
- * JOB_RESTART jobs on the other hand can be merged, because they get
- * patched into JOB_START after stopping the unit. So if we see a
- * JOB_RESTART running, it means the unit hasn't stopped yet and at
- * this time the merge is still allowed. */
- return t != JOB_RELOAD;
+ * Should the second reload job be merged into the first one, the daemon would not know about the new config.
+ * JOB_RESTART jobs on the other hand can be merged, because they get patched into JOB_START
+ * after stopping the unit. So if we see a JOB_RESTART running, it means the unit hasn't stopped yet
+ * and at this time the merge is still allowed. */
+ if (j->type == JOB_RELOAD)
+ return false;
+
+ return job_type_is_superset(uj->type, j->type);
}
static void job_merge_into_installed(Job *j, Job *other) {
+ assert(j);
assert(j->installed);
+ assert(other);
assert(j->unit == other->unit);
if (j->type != JOB_NOP) {
j->ignore_order = j->ignore_order || other->ignore_order;
}
-Job* job_install(Job *j, bool refuse_late_merge) {
+Job* job_install(Job *j) {
Job **pj;
Job *uj;
assert(j);
assert(!j->installed);
- assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+ assert(j->type >= 0 && j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
assert(j->state == JOB_WAITING);
pj = j->type == JOB_NOP ? &j->unit->nop_job : &j->unit->job;
else {
/* not conflicting, i.e. mergeable */
- if (uj->state == JOB_WAITING ||
- (!refuse_late_merge && job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
+ if (uj->state == JOB_WAITING || jobs_may_late_merge(j, uj)) {
job_merge_into_installed(uj, j);
log_unit_debug(uj->unit,
"Merged %s/%s into installed job %s/%s as %"PRIu32,
bool installed:1;
bool in_run_queue:1;
+
bool matters_to_anchor:1;
- bool in_dbus_queue:1;
- bool sent_dbus_new_signal:1;
+ bool refuse_late_merge:1;
bool ignore_order:1;
bool irreversible:1;
- bool in_gc_queue:1;
+
+ bool in_dbus_queue:1;
+ bool sent_dbus_new_signal:1;
+
bool ref_by_private_bus:1;
+
+ bool in_gc_queue:1;
};
Job* job_new(Unit *unit, JobType type);
Job* job_free(Job *job);
DEFINE_TRIVIAL_CLEANUP_FUNC(Job*, job_free);
-Job* job_install(Job *j, bool refuse_late_merge);
+Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "transaction.h"
#include "unit-name.h"
#include "unit.h"
#include "utf8.h"
}
/* Any units that are bound to this service must also be restarted. We use JOB_START for ourselves
- * but then set JOB_RESTART_DEPENDENCIES which will enqueue JOB_RESTART for those dependency jobs. */
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(s), JOB_RESTART_DEPENDENCIES, &error, /* ret = */ NULL);
+ * but then set JOB_RESTART_DEPENDENCIES which will enqueue JOB_RESTART for those dependency jobs.
+ *
+ * When RestartMode=direct is used, the service being restarted don't enter the inactive/failed state,
+ * i.e. unit_process_job -> job_finish_and_invalidate is never called, and the previous job might still
+ * be running (especially for Type=oneshot services).
+ * We need to refuse late merge and re-enqueue the anchor job. */
+ r = manager_add_job_full(UNIT(s)->manager,
+ JOB_START, UNIT(s),
+ JOB_RESTART_DEPENDENCIES,
+ TRANSACTION_REENQUEUE_ANCHOR,
+ /* affected_jobs = */ NULL,
+ &error, /* ret = */ NULL);
if (r < 0) {
log_unit_warning(UNIT(s), "Failed to schedule restart job: %s", bus_error_message(&error, r));
return service_enter_dead(s, SERVICE_FAILURE_RESOURCES, /* allow_restart= */ false);
Job *j;
int r;
+ assert(tr);
+ assert(m);
+
/* Moves the transaction jobs to the set of active jobs */
if (IN_SET(mode, JOB_ISOLATE, JOB_FLUSH)) {
/* Clean the job dependencies */
transaction_unlink_job(tr, j, false);
- /* When RestartMode=direct is used, the service being restarted don't enter the inactive/failed
- * state, i.e. unit_process_job -> job_finish_and_invalidate is never called, and the previous
- * job might still be running (especially for Type=oneshot services). We need to refuse
- * late merge and re-enqueue the anchor job. */
- installed_job = job_install(j,
- /* refuse_late_merge = */ mode == JOB_RESTART_DEPENDENCIES && j == tr->anchor_job);
+ installed_job = job_install(j);
if (installed_job != j) {
/* j has been merged into a previously installed job */
if (tr->anchor_job == j)
int r;
unsigned generation = 1;
- assert(tr);
+ /* This applies the changes recorded in tr->jobs to the actual list of jobs, if possible. */
- /* This applies the changes recorded in tr->jobs to
- * the actual list of jobs, if possible. */
+ assert(tr);
+ assert(m);
/* Reset the generation counter of all installed jobs. The detection of cycles
* looks at installed jobs. If they had a non-zero generation from some previous
r = transaction_verify_order(tr, &generation, e);
if (r >= 0)
break;
-
if (r != -EAGAIN)
return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
r = transaction_merge_jobs(tr, e);
if (r >= 0)
break;
-
if (r != -EAGAIN)
return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
if (!j)
return NULL;
- j->generation = 0;
- j->marker = NULL;
- j->matters_to_anchor = false;
j->irreversible = tr->irreversible;
LIST_PREPEND(transaction, f, j);
int r;
assert(tr);
+ assert(type >= 0);
assert(type < _JOB_TYPE_MAX);
assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
assert(unit);
/* If the job has no parent job, it is the anchor job. */
assert(!tr->anchor_job);
tr->anchor_job = ret;
+
+ if (FLAGS_SET(flags, TRANSACTION_REENQUEUE_ANCHOR))
+ ret->refuse_late_merge = true;
}
if (!is_new || FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) || type == JOB_NOP)
/* Indicate that we're in the recursion for processing UNIT_ATOM_PROPAGATE_STOP_GRACEFUL units */
TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL = 1 << 5,
- _TRANSACTION_FLAGS_MASK_PUBLIC = 0,
+ /* Always re-enqueue anchor job (refuse late merge) */
+ TRANSACTION_REENQUEUE_ANCHOR = 1 << 6,
+
+ _TRANSACTION_FLAGS_MASK_PUBLIC = TRANSACTION_REENQUEUE_ANCHOR,
} TransactionAddFlags;
void transaction_add_propagate_reload_jobs(