assert(tr);
while ((j = hashmap_first(tr->jobs)))
- transaction_delete_job(tr, j, true);
+ transaction_delete_job(tr, j, false);
assert(hashmap_isempty(tr->jobs));
}
}
static void transaction_drop_redundant(Transaction *tr) {
- bool again;
-
- assert(tr);
-
- /* Goes through the transaction and removes all jobs that are
- * a noop */
-
- do {
- Job *j;
- Iterator i;
-
- again = false;
-
- HASHMAP_FOREACH(j, tr->jobs, i) {
- bool changes_something = false;
- Job *k;
+ Job *j;
+ Iterator i;
- LIST_FOREACH(transaction, k, j) {
+ /* Goes through the transaction and removes all jobs of the units
+ * whose jobs are all noops. If not all of a unit's jobs are
+ * redundant, they are kept. */
- if (tr->anchor_job != k &&
- job_type_is_redundant(k->type, unit_active_state(k->unit)) &&
- (!k->unit->job || !job_type_is_conflicting(k->type, k->unit->job->type)))
- continue;
+ assert(tr);
- changes_something = true;
- break;
- }
+rescan:
+ HASHMAP_FOREACH(j, tr->jobs, i) {
+ Job *k;
- if (changes_something)
- continue;
+ LIST_FOREACH(transaction, k, j) {
- /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
- transaction_delete_job(tr, j, false);
- again = true;
- break;
+ if (tr->anchor_job == k ||
+ !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
+ (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
+ goto next_unit;
}
- } while (again);
+ /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
+ transaction_delete_job(tr, j, false);
+ goto rescan;
+ next_unit:;
+ }
}
static bool unit_matters_to_anchor(Unit *u, Job *j) {
}
static void transaction_collect_garbage(Transaction *tr) {
- bool again;
+ Iterator i;
+ Job *j;
assert(tr);
/* Drop jobs that are not required by any other job */
- do {
- Iterator i;
- Job *j;
-
- again = false;
-
- HASHMAP_FOREACH(j, tr->jobs, i) {
- if (tr->anchor_job == j || j->object_list) {
- /* log_debug("Keeping job %s/%s because of %s/%s", */
- /* j->unit->id, job_type_to_string(j->type), */
- /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
- /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
- continue;
- }
-
- /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
- transaction_delete_job(tr, j, true);
- again = true;
- break;
+rescan:
+ HASHMAP_FOREACH(j, tr->jobs, i) {
+ if (tr->anchor_job == j || j->object_list) {
+ /* log_debug("Keeping job %s/%s because of %s/%s", */
+ /* j->unit->id, job_type_to_string(j->type), */
+ /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
+ /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
+ continue;
}
- } while (again);
+ /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
+ transaction_delete_job(tr, j, true);
+ goto rescan;
+ }
}
static int transaction_is_destructive(Transaction *tr, DBusError *e) {
}
static void transaction_minimize_impact(Transaction *tr) {
- bool again;
+ Job *j;
+ Iterator i;
+
assert(tr);
/* Drops all unnecessary jobs that reverse already active jobs
* or that stop a running service. */
- do {
- Job *j;
- Iterator i;
-
- again = false;
-
- HASHMAP_FOREACH(j, tr->jobs, i) {
- LIST_FOREACH(transaction, j, j) {
- bool stops_running_service, changes_existing_job;
-
- /* If it matters, we shouldn't drop it */
- if (j->matters_to_anchor)
- continue;
+rescan:
+ HASHMAP_FOREACH(j, tr->jobs, i) {
+ LIST_FOREACH(transaction, j, j) {
+ bool stops_running_service, changes_existing_job;
- /* Would this stop a running service?
- * Would this change an existing job?
- * If so, let's drop this entry */
+ /* If it matters, we shouldn't drop it */
+ if (j->matters_to_anchor)
+ continue;
- stops_running_service =
- j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
+ /* Would this stop a running service?
+ * Would this change an existing job?
+ * If so, let's drop this entry */
- changes_existing_job =
- j->unit->job &&
- job_type_is_conflicting(j->type, j->unit->job->type);
+ stops_running_service =
+ j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
- if (!stops_running_service && !changes_existing_job)
- continue;
+ changes_existing_job =
+ j->unit->job &&
+ job_type_is_conflicting(j->type, j->unit->job->type);
- if (stops_running_service)
- log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
+ if (!stops_running_service && !changes_existing_job)
+ continue;
- if (changes_existing_job)
- log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
+ if (stops_running_service)
+ log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
- /* Ok, let's get rid of this */
- log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
+ if (changes_existing_job)
+ log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
- transaction_delete_job(tr, j, true);
- again = true;
- break;
- }
+ /* Ok, let's get rid of this */
+ log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
- if (again)
- break;
+ transaction_delete_job(tr, j, true);
+ goto rescan;
}
-
- } while (again);
+ }
}
static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
/* When isolating first kill all installed jobs which
* aren't part of the new transaction */
- rescan:
HASHMAP_FOREACH(j, m->jobs, i) {
assert(j->installed);
if (hashmap_get(tr->jobs, j->unit))
continue;
- /* 'j' itself is safe to remove, but if other jobs
- are invalidated recursively, our iterator may become
- invalid and we need to start over. */
- if (job_finish_and_invalidate(j, JOB_CANCELED) > 0)
- goto rescan;
+ /* Not invalidating recursively. Avoids triggering
+ * OnFailure= actions of dependent jobs. Also avoids
+ * invalidating our iterator. */
+ job_finish_and_invalidate(j, JOB_CANCELED, false);
}
}
}
int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
+ Iterator i;
+ Job *j;
int r;
unsigned generation = 1;
/* This applies the changes recorded in tr->jobs to
* the actual list of jobs, if possible. */
+ /* 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
+ * walk of the graph, the algorithm would break. */
+ HASHMAP_FOREACH(j, m->jobs, i)
+ j->generation = 0;
+
/* First step: figure out which jobs matter */
transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);