summaryrefslogtreecommitdiff
path: root/src/core/job.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/job.c')
-rw-r--r--src/core/job.c58
1 files changed, 55 insertions, 3 deletions
diff --git a/src/core/job.c b/src/core/job.c
index 0b50888184..3454ffd1db 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -96,18 +96,70 @@ void job_uninstall(Job *j) {
j->installed = false;
}
-void job_install(Job *j) {
+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.
+ * 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 || t == JOB_RELOAD_OR_START);
+}
+
+static void job_merge_into_installed(Job *j, Job *other) {
+ assert(j->installed);
+ assert(j->unit == other->unit);
+
+ j->type = job_type_lookup_merge(j->type, other->type);
+ assert(j->type >= 0);
+
+ j->override = j->override || other->override;
+}
+
+Job* job_install(Job *j) {
Job *uj = j->unit->job;
+ assert(!j->installed);
+
if (uj) {
- job_uninstall(uj);
- job_free(uj);
+ if (job_type_is_conflicting(uj->type, j->type))
+ job_finish_and_invalidate(uj, JOB_CANCELED);
+ else {
+ /* not conflicting, i.e. mergeable */
+
+ if (uj->state == JOB_WAITING ||
+ (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
+ job_merge_into_installed(uj, j);
+ log_debug("Merged into installed job %s/%s as %u",
+ uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ return uj;
+ } else {
+ /* already running and not safe to merge into */
+ /* Patch uj to become a merged job and re-run it. */
+ /* XXX It should be safer to queue j to run after uj finishes, but it is
+ * not currently possible to have more than one installed job per unit. */
+ job_merge_into_installed(uj, j);
+ log_debug("Merged into running job, re-running: %s/%s as %u",
+ uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ uj->state = JOB_WAITING;
+ return uj;
+ }
+ }
}
+ /* Install the job */
j->unit->job = j;
j->installed = true;
j->manager->n_installed_jobs ++;
log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ return j;
}
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {