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