xref: /aosp_15_r20/external/cronet/base/task/post_job.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2019 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_TASK_POST_JOB_H_
6*6777b538SAndroid Build Coastguard Worker #define BASE_TASK_POST_JOB_H_
7*6777b538SAndroid Build Coastguard Worker 
8*6777b538SAndroid Build Coastguard Worker #include <limits>
9*6777b538SAndroid Build Coastguard Worker 
10*6777b538SAndroid Build Coastguard Worker #include "base/base_export.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/dcheck_is_on.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/functional/callback.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/location.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/memory/stack_allocated.h"
15*6777b538SAndroid Build Coastguard Worker 
16*6777b538SAndroid Build Coastguard Worker namespace base {
17*6777b538SAndroid Build Coastguard Worker namespace internal {
18*6777b538SAndroid Build Coastguard Worker class JobTaskSource;
19*6777b538SAndroid Build Coastguard Worker class PooledTaskRunnerDelegate;
20*6777b538SAndroid Build Coastguard Worker }
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker class TaskTraits;
23*6777b538SAndroid Build Coastguard Worker enum class TaskPriority : uint8_t;
24*6777b538SAndroid Build Coastguard Worker 
25*6777b538SAndroid Build Coastguard Worker // Delegate that's passed to Job's worker task, providing an entry point to
26*6777b538SAndroid Build Coastguard Worker // communicate with the scheduler. To prevent deadlocks, JobDelegate methods
27*6777b538SAndroid Build Coastguard Worker // should never be called while holding a user lock.
28*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT JobDelegate {
29*6777b538SAndroid Build Coastguard Worker   STACK_ALLOCATED();
30*6777b538SAndroid Build Coastguard Worker 
31*6777b538SAndroid Build Coastguard Worker  public:
32*6777b538SAndroid Build Coastguard Worker   // A JobDelegate is instantiated for each worker task that is run.
33*6777b538SAndroid Build Coastguard Worker   // |task_source| is the task source whose worker task is running with this
34*6777b538SAndroid Build Coastguard Worker   // delegate and |pooled_task_runner_delegate| is used by ShouldYield() to
35*6777b538SAndroid Build Coastguard Worker   // check whether the pool wants this worker task to yield (null if this worker
36*6777b538SAndroid Build Coastguard Worker   // should never yield -- e.g. when the main thread is a worker).
37*6777b538SAndroid Build Coastguard Worker   JobDelegate(internal::JobTaskSource* task_source,
38*6777b538SAndroid Build Coastguard Worker               internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate);
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker   JobDelegate(const JobDelegate&) = delete;
41*6777b538SAndroid Build Coastguard Worker   JobDelegate& operator=(const JobDelegate&) = delete;
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker   ~JobDelegate();
44*6777b538SAndroid Build Coastguard Worker 
45*6777b538SAndroid Build Coastguard Worker   // Returns true if this thread *must* return from the worker task on the
46*6777b538SAndroid Build Coastguard Worker   // current thread ASAP. Workers should periodically invoke ShouldYield (or
47*6777b538SAndroid Build Coastguard Worker   // YieldIfNeeded()) as often as is reasonable.
48*6777b538SAndroid Build Coastguard Worker   bool ShouldYield();
49*6777b538SAndroid Build Coastguard Worker 
50*6777b538SAndroid Build Coastguard Worker   // If ShouldYield(), this will pause the current thread (allowing it to be
51*6777b538SAndroid Build Coastguard Worker   // replaced in the pool); no-ops otherwise. If it pauses, it will resume and
52*6777b538SAndroid Build Coastguard Worker   // return from this call whenever higher priority work completes.
53*6777b538SAndroid Build Coastguard Worker   // Prefer ShouldYield() over this (only use YieldIfNeeded() when unwinding
54*6777b538SAndroid Build Coastguard Worker   // the stack is not possible).
55*6777b538SAndroid Build Coastguard Worker   void YieldIfNeeded();
56*6777b538SAndroid Build Coastguard Worker 
57*6777b538SAndroid Build Coastguard Worker   // Notifies the scheduler that max concurrency was increased, and the number
58*6777b538SAndroid Build Coastguard Worker   // of worker should be adjusted accordingly. See PostJob() for more details.
59*6777b538SAndroid Build Coastguard Worker   void NotifyConcurrencyIncrease();
60*6777b538SAndroid Build Coastguard Worker 
61*6777b538SAndroid Build Coastguard Worker   // Returns a task_id unique among threads currently running this job, such
62*6777b538SAndroid Build Coastguard Worker   // that GetTaskId() < worker count. To achieve this, the same task_id may be
63*6777b538SAndroid Build Coastguard Worker   // reused by a different thread after a worker_task returns.
64*6777b538SAndroid Build Coastguard Worker   uint8_t GetTaskId();
65*6777b538SAndroid Build Coastguard Worker 
66*6777b538SAndroid Build Coastguard Worker   // Returns true if the current task is called from the thread currently
67*6777b538SAndroid Build Coastguard Worker   // running JobHandle::Join().
IsJoiningThread()68*6777b538SAndroid Build Coastguard Worker   bool IsJoiningThread() const {
69*6777b538SAndroid Build Coastguard Worker     return pooled_task_runner_delegate_ == nullptr;
70*6777b538SAndroid Build Coastguard Worker   }
71*6777b538SAndroid Build Coastguard Worker 
72*6777b538SAndroid Build Coastguard Worker  private:
73*6777b538SAndroid Build Coastguard Worker   static constexpr uint8_t kInvalidTaskId = std::numeric_limits<uint8_t>::max();
74*6777b538SAndroid Build Coastguard Worker 
75*6777b538SAndroid Build Coastguard Worker   internal::JobTaskSource* task_source_ = nullptr;
76*6777b538SAndroid Build Coastguard Worker   internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate_ = nullptr;
77*6777b538SAndroid Build Coastguard Worker   uint8_t task_id_ = kInvalidTaskId;
78*6777b538SAndroid Build Coastguard Worker 
79*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
80*6777b538SAndroid Build Coastguard Worker   // Value returned by the last call to ShouldYield().
81*6777b538SAndroid Build Coastguard Worker   bool last_should_yield_ = false;
82*6777b538SAndroid Build Coastguard Worker #endif
83*6777b538SAndroid Build Coastguard Worker };
84*6777b538SAndroid Build Coastguard Worker 
85*6777b538SAndroid Build Coastguard Worker // Handle returned when posting a Job. Provides methods to control execution of
86*6777b538SAndroid Build Coastguard Worker // the posted Job. To prevent deadlocks, JobHandle methods should never be
87*6777b538SAndroid Build Coastguard Worker // called while holding a user lock.
88*6777b538SAndroid Build Coastguard Worker class BASE_EXPORT JobHandle {
89*6777b538SAndroid Build Coastguard Worker  public:
90*6777b538SAndroid Build Coastguard Worker   JobHandle();
91*6777b538SAndroid Build Coastguard Worker 
92*6777b538SAndroid Build Coastguard Worker   JobHandle(const JobHandle&) = delete;
93*6777b538SAndroid Build Coastguard Worker   JobHandle& operator=(const JobHandle&) = delete;
94*6777b538SAndroid Build Coastguard Worker 
95*6777b538SAndroid Build Coastguard Worker   // A job must either be joined, canceled or detached before the JobHandle is
96*6777b538SAndroid Build Coastguard Worker   // destroyed.
97*6777b538SAndroid Build Coastguard Worker   ~JobHandle();
98*6777b538SAndroid Build Coastguard Worker 
99*6777b538SAndroid Build Coastguard Worker   JobHandle(JobHandle&&);
100*6777b538SAndroid Build Coastguard Worker   JobHandle& operator=(JobHandle&&);
101*6777b538SAndroid Build Coastguard Worker 
102*6777b538SAndroid Build Coastguard Worker   // Returns true if associated with a Job.
103*6777b538SAndroid Build Coastguard Worker   explicit operator bool() const { return task_source_ != nullptr; }
104*6777b538SAndroid Build Coastguard Worker 
105*6777b538SAndroid Build Coastguard Worker   // Returns true if there's any work pending or any worker running.
106*6777b538SAndroid Build Coastguard Worker   bool IsActive() const;
107*6777b538SAndroid Build Coastguard Worker 
108*6777b538SAndroid Build Coastguard Worker   // Update this Job's priority.
109*6777b538SAndroid Build Coastguard Worker   void UpdatePriority(TaskPriority new_priority);
110*6777b538SAndroid Build Coastguard Worker 
111*6777b538SAndroid Build Coastguard Worker   // Notifies the scheduler that max concurrency was increased, and the number
112*6777b538SAndroid Build Coastguard Worker   // of workers should be adjusted accordingly. See PostJob() for more details.
113*6777b538SAndroid Build Coastguard Worker   void NotifyConcurrencyIncrease();
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker   // Contributes to the job on this thread. Doesn't return until all tasks have
116*6777b538SAndroid Build Coastguard Worker   // completed and max concurrency becomes 0. This also promotes this Job's
117*6777b538SAndroid Build Coastguard Worker   // priority to be at least as high as the calling thread's priority. When
118*6777b538SAndroid Build Coastguard Worker   // called immediately, prefer CreateJob(...).Join() over PostJob(...).Join()
119*6777b538SAndroid Build Coastguard Worker   // to avoid having too many workers scheduled for executing the workload.
120*6777b538SAndroid Build Coastguard Worker   void Join();
121*6777b538SAndroid Build Coastguard Worker 
122*6777b538SAndroid Build Coastguard Worker   // Forces all existing workers to yield ASAP. Waits until they have all
123*6777b538SAndroid Build Coastguard Worker   // returned from the Job's callback before returning.
124*6777b538SAndroid Build Coastguard Worker   void Cancel();
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker   // Forces all existing workers to yield ASAP but doesn’t wait for them.
127*6777b538SAndroid Build Coastguard Worker   // Warning, this is dangerous if the Job's callback is bound to or has access
128*6777b538SAndroid Build Coastguard Worker   // to state which may be deleted after this call.
129*6777b538SAndroid Build Coastguard Worker   void CancelAndDetach();
130*6777b538SAndroid Build Coastguard Worker 
131*6777b538SAndroid Build Coastguard Worker   // Can be invoked before ~JobHandle() to avoid waiting on the job completing.
132*6777b538SAndroid Build Coastguard Worker   void Detach();
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker  private:
135*6777b538SAndroid Build Coastguard Worker   friend class internal::JobTaskSource;
136*6777b538SAndroid Build Coastguard Worker 
137*6777b538SAndroid Build Coastguard Worker   explicit JobHandle(scoped_refptr<internal::JobTaskSource> task_source);
138*6777b538SAndroid Build Coastguard Worker 
139*6777b538SAndroid Build Coastguard Worker   scoped_refptr<internal::JobTaskSource> task_source_;
140*6777b538SAndroid Build Coastguard Worker };
141*6777b538SAndroid Build Coastguard Worker 
142*6777b538SAndroid Build Coastguard Worker // Callback used in PostJob() to control the maximum number of threads calling
143*6777b538SAndroid Build Coastguard Worker // the worker task concurrently.
144*6777b538SAndroid Build Coastguard Worker 
145*6777b538SAndroid Build Coastguard Worker // Returns the maximum number of threads which may call a job's worker task
146*6777b538SAndroid Build Coastguard Worker // concurrently. |worker_count| is the number of threads currently assigned to
147*6777b538SAndroid Build Coastguard Worker // this job which some callers may need to determine their return value.
148*6777b538SAndroid Build Coastguard Worker using MaxConcurrencyCallback =
149*6777b538SAndroid Build Coastguard Worker     RepeatingCallback<size_t(size_t /*worker_count*/)>;
150*6777b538SAndroid Build Coastguard Worker 
151*6777b538SAndroid Build Coastguard Worker // Posts a repeating |worker_task| with specific |traits| to run in parallel on
152*6777b538SAndroid Build Coastguard Worker // base::ThreadPool.
153*6777b538SAndroid Build Coastguard Worker // Returns a JobHandle associated with the Job, which can be joined, canceled or
154*6777b538SAndroid Build Coastguard Worker // detached.
155*6777b538SAndroid Build Coastguard Worker // ThreadPool APIs, including PostJob() and methods of the returned JobHandle,
156*6777b538SAndroid Build Coastguard Worker // must never be called while holding a lock that could be acquired by
157*6777b538SAndroid Build Coastguard Worker // |worker_task| or |max_concurrency_callback| -- that could result in a
158*6777b538SAndroid Build Coastguard Worker // deadlock. This is because [1] |max_concurrency_callback| may be invoked while
159*6777b538SAndroid Build Coastguard Worker // holding internal ThreadPool lock (A), hence |max_concurrency_callback| can
160*6777b538SAndroid Build Coastguard Worker // only use a lock (B) if that lock is *never* held while calling back into a
161*6777b538SAndroid Build Coastguard Worker // ThreadPool entry point from any thread (A=>B/B=>A deadlock) and [2]
162*6777b538SAndroid Build Coastguard Worker // |worker_task| or |max_concurrency_callback| is invoked synchronously from
163*6777b538SAndroid Build Coastguard Worker // JobHandle::Join() (A=>JobHandle::Join()=>A deadlock).
164*6777b538SAndroid Build Coastguard Worker // To avoid scheduling overhead, |worker_task| should do as much work as
165*6777b538SAndroid Build Coastguard Worker // possible in a loop when invoked, and JobDelegate::ShouldYield() should be
166*6777b538SAndroid Build Coastguard Worker // periodically invoked to conditionally exit and let the scheduler prioritize
167*6777b538SAndroid Build Coastguard Worker // work.
168*6777b538SAndroid Build Coastguard Worker //
169*6777b538SAndroid Build Coastguard Worker // A canonical implementation of |worker_task| looks like:
170*6777b538SAndroid Build Coastguard Worker //   void WorkerTask(JobDelegate* job_delegate) {
171*6777b538SAndroid Build Coastguard Worker //     while (!job_delegate->ShouldYield()) {
172*6777b538SAndroid Build Coastguard Worker //       auto work_item = worker_queue.TakeWorkItem(); // Smallest unit of work.
173*6777b538SAndroid Build Coastguard Worker //       if (!work_item)
174*6777b538SAndroid Build Coastguard Worker //         return:
175*6777b538SAndroid Build Coastguard Worker //       ProcessWork(work_item);
176*6777b538SAndroid Build Coastguard Worker //     }
177*6777b538SAndroid Build Coastguard Worker //   }
178*6777b538SAndroid Build Coastguard Worker //
179*6777b538SAndroid Build Coastguard Worker // |max_concurrency_callback| controls the maximum number of threads calling
180*6777b538SAndroid Build Coastguard Worker // |worker_task| concurrently. |worker_task| is only invoked if the number of
181*6777b538SAndroid Build Coastguard Worker // threads previously running |worker_task| was less than the value returned by
182*6777b538SAndroid Build Coastguard Worker // |max_concurrency_callback|. In general, |max_concurrency_callback| should
183*6777b538SAndroid Build Coastguard Worker // return the latest number of incomplete work items (smallest unit of work)
184*6777b538SAndroid Build Coastguard Worker // left to processed. JobHandle/JobDelegate::NotifyConcurrencyIncrease() *must*
185*6777b538SAndroid Build Coastguard Worker // be invoked shortly after |max_concurrency_callback| starts returning a value
186*6777b538SAndroid Build Coastguard Worker // larger than previously returned values. This usually happens when new work
187*6777b538SAndroid Build Coastguard Worker // items are added and the API user wants additional threads to invoke
188*6777b538SAndroid Build Coastguard Worker // |worker_task| concurrently. The callbacks may be called concurrently on any
189*6777b538SAndroid Build Coastguard Worker // thread until the job is complete. If the job handle is detached, the
190*6777b538SAndroid Build Coastguard Worker // callbacks may still be called, so they must not access global state that
191*6777b538SAndroid Build Coastguard Worker // could be destroyed.
192*6777b538SAndroid Build Coastguard Worker //
193*6777b538SAndroid Build Coastguard Worker // |traits| requirements:
194*6777b538SAndroid Build Coastguard Worker // - base::ThreadPolicy must be specified if the priority of the task runner
195*6777b538SAndroid Build Coastguard Worker //   will ever be increased from BEST_EFFORT.
196*6777b538SAndroid Build Coastguard Worker JobHandle BASE_EXPORT PostJob(const Location& from_here,
197*6777b538SAndroid Build Coastguard Worker                               const TaskTraits& traits,
198*6777b538SAndroid Build Coastguard Worker                               RepeatingCallback<void(JobDelegate*)> worker_task,
199*6777b538SAndroid Build Coastguard Worker                               MaxConcurrencyCallback max_concurrency_callback);
200*6777b538SAndroid Build Coastguard Worker 
201*6777b538SAndroid Build Coastguard Worker // Creates and returns a JobHandle associated with a Job. Unlike PostJob(), this
202*6777b538SAndroid Build Coastguard Worker // doesn't immediately schedules |worker_task| to run on base::ThreadPool
203*6777b538SAndroid Build Coastguard Worker // workers; the Job is then scheduled by calling either
204*6777b538SAndroid Build Coastguard Worker // NotifyConcurrencyIncrease() or Join().
205*6777b538SAndroid Build Coastguard Worker JobHandle BASE_EXPORT
206*6777b538SAndroid Build Coastguard Worker CreateJob(const Location& from_here,
207*6777b538SAndroid Build Coastguard Worker           const TaskTraits& traits,
208*6777b538SAndroid Build Coastguard Worker           RepeatingCallback<void(JobDelegate*)> worker_task,
209*6777b538SAndroid Build Coastguard Worker           MaxConcurrencyCallback max_concurrency_callback);
210*6777b538SAndroid Build Coastguard Worker 
211*6777b538SAndroid Build Coastguard Worker }  // namespace base
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker #endif  // BASE_TASK_POST_JOB_H_
214