[ThreadPool]: Add comment in post_job.h to avoid holding user lock.
Api should never be called while holding user lock.
Since user is the leaf, that can cause deadlock.
Bug: 839091
Change-Id: I30ec549b52d259348814fe040c393732640f229a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151864
Reviewed-by: François Doray <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Commit-Queue: Etienne Pierre-Doray <[email protected]>
Cr-Commit-Position: refs/heads/master@{#760634}
diff --git a/base/task/post_job.h b/base/task/post_job.h
index fe791cf..1d396f1 100644
--- a/base/task/post_job.h
+++ b/base/task/post_job.h
@@ -21,7 +21,8 @@
}
// Delegate that's passed to Job's worker task, providing an entry point to
-// communicate with the scheduler.
+// communicate with the scheduler. To prevent deadlocks, JobDelegate methods
+// should never be called while holding a user lock.
class BASE_EXPORT JobDelegate {
public:
// A JobDelegate is instantiated for each worker task that is run.
@@ -72,7 +73,8 @@
};
// Handle returned when posting a Job. Provides methods to control execution of
-// the posted Job.
+// the posted Job. To prevent deadlocks, JobHandle methods should never be
+// called while holding a user lock.
class BASE_EXPORT JobHandle {
public:
JobHandle();
@@ -124,6 +126,15 @@
// base::ThreadPool.
// Returns a JobHandle associated with the Job, which can be joined, canceled or
// detached.
+// ThreadPool APIs, including PostJob() and methods of the returned JobHandle,
+// must never be called while holding a lock that could be acquired by
+// |worker_task| or |max_concurrency_callback| -- that could result in a
+// deadlock. This is because [1] |max_concurrency_callback| may be invoked while
+// holding internal ThreadPool lock (A), hence |max_concurrency_callback| can
+// only use a lock (B) if that lock is *never* held while calling back into a
+// ThreadPool entry point from any thread (A=>B/B=>A deadlock) and [2]
+// |worker_task| or |max_concurrency_callback| is invoked synchronously from
+// JobHandle::Join() (A=>JobHandle::Join()=>A deadlock).
// To avoid scheduling overhead, |worker_task| should do as much work as
// possible in a loop when invoked, and JobDelegate::ShouldYield() should be
// periodically invoked to conditionally exit and let the scheduler prioritize