Prevent potential deadlock when canceling jobs.

This commit is contained in:
Cody Henthorne
2025-08-14 16:17:09 -04:00
committed by Jeffrey Starke
parent 6f051ce4c2
commit 2b56e00e89

View File

@@ -206,7 +206,11 @@ class JobController {
}
@WorkerThread
synchronized void cancelJob(@NonNull String id) {
void cancelJob(@NonNull String id) {
Job inactiveJob = null;
List<Job> inactiveJobDependents = Collections.emptyList();
synchronized (this) {
Job runningJob = runningJobs.get(id);
if (runningJob != null) {
@@ -216,23 +220,33 @@ class JobController {
JobSpec jobSpec = jobStorage.getJobSpec(id);
if (jobSpec != null) {
Job job = createJob(jobSpec, jobStorage.getConstraintSpecs(id));
Log.w(TAG, JobLogger.format(job, "Canceling while inactive."));
Log.w(TAG, JobLogger.format(job, "Job failed."));
inactiveJob = createJob(jobSpec, jobStorage.getConstraintSpecs(id));
Log.w(TAG, JobLogger.format(inactiveJob, "Canceling while inactive."));
Log.w(TAG, JobLogger.format(inactiveJob, "Job failed."));
job.cancel();
List<Job> dependents = onFailure(job);
job.onFailure();
Stream.of(dependents).forEach(Job::onFailure);
inactiveJob.cancel();
inactiveJobDependents = onFailure(inactiveJob);
} else {
Log.w(TAG, "Tried to cancel JOB::" + id + ", but it could not be found.");
}
}
}
// We have no control over what happens in jobs' onFailure method, so we drop our lock to reduce the possibility of a deadlock
if (inactiveJob != null) {
inactiveJob.onFailure();
Stream.of(inactiveJobDependents).forEach(Job::onFailure);
}
}
@WorkerThread
synchronized void cancelAllInQueue(@NonNull String queue) {
Stream.of(jobStorage.getJobsInQueue(queue))
void cancelAllInQueue(@NonNull String queue) {
List<JobSpec> jobsInQueue;
synchronized (this) {
jobsInQueue = jobStorage.getJobsInQueue(queue);
}
Stream.of(jobsInQueue)
.map(JobSpec::getId)
.forEach(this::cancelJob);
}