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,33 +206,47 @@ class JobController {
} }
@WorkerThread @WorkerThread
synchronized void cancelJob(@NonNull String id) { void cancelJob(@NonNull String id) {
Job runningJob = runningJobs.get(id); Job inactiveJob = null;
List<Job> inactiveJobDependents = Collections.emptyList();
if (runningJob != null) { synchronized (this) {
Log.w(TAG, JobLogger.format(runningJob, "Canceling while running.")); Job runningJob = runningJobs.get(id);
runningJob.cancel();
} else {
JobSpec jobSpec = jobStorage.getJobSpec(id);
if (jobSpec != null) { if (runningJob != null) {
Job job = createJob(jobSpec, jobStorage.getConstraintSpecs(id)); Log.w(TAG, JobLogger.format(runningJob, "Canceling while running."));
Log.w(TAG, JobLogger.format(job, "Canceling while inactive.")); runningJob.cancel();
Log.w(TAG, JobLogger.format(job, "Job failed."));
job.cancel();
List<Job> dependents = onFailure(job);
job.onFailure();
Stream.of(dependents).forEach(Job::onFailure);
} else { } else {
Log.w(TAG, "Tried to cancel JOB::" + id + ", but it could not be found."); JobSpec jobSpec = jobStorage.getJobSpec(id);
if (jobSpec != null) {
inactiveJob = createJob(jobSpec, jobStorage.getConstraintSpecs(id));
Log.w(TAG, JobLogger.format(inactiveJob, "Canceling while inactive."));
Log.w(TAG, JobLogger.format(inactiveJob, "Job failed."));
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 @WorkerThread
synchronized void cancelAllInQueue(@NonNull String queue) { void cancelAllInQueue(@NonNull String queue) {
Stream.of(jobStorage.getJobsInQueue(queue)) List<JobSpec> jobsInQueue;
synchronized (this) {
jobsInQueue = jobStorage.getJobsInQueue(queue);
}
Stream.of(jobsInQueue)
.map(JobSpec::getId) .map(JobSpec::getId)
.forEach(this::cancelJob); .forEach(this::cancelJob);
} }