blob: a00fd989c51cc8837cf3dbd59cfa10531caf3b99 [file] [log] [blame]
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:381// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Gabriel Charette52fa3ae2019-04-15 21:44:375#ifndef BASE_TASK_THREAD_POOL_TASK_SOURCE_H_
6#define BASE_TASK_THREAD_POOL_TASK_SOURCE_H_
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:387
8#include <stddef.h>
9
10#include "base/base_export.h"
Patrick Monette8a0eaaa2021-10-01 20:53:0011#include "base/containers/intrusive_heap.h"
David Sandersfc1f17fa2022-04-15 00:15:4912#include "base/dcheck_is_on.h"
Keishi Hattori0e45c022021-11-27 09:25:5213#include "base/memory/raw_ptr.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3814#include "base/memory/ref_counted.h"
Etienne Pierre-doray312462152019-03-19 16:10:1715#include "base/sequence_token.h"
Gabriel Charetted35648382019-04-30 21:10:5916#include "base/task/common/checked_lock.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3817#include "base/task/task_traits.h"
Gabriel Charette52fa3ae2019-04-15 21:44:3718#include "base/task/thread_pool/task.h"
Etienne Pierre-dorayeed66832020-09-02 01:47:1119#include "base/task/thread_pool/task_source_sort_key.h"
Etienne Pierre-doray312462152019-03-19 16:10:1720#include "base/threading/sequence_local_storage_map.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3821
22namespace base {
23namespace internal {
24
Etienne Pierre-dorayf7f59c32019-05-24 16:50:5325class TaskTracker;
26
Etienne Pierre-doray45fd5d52019-03-28 15:19:5527enum class TaskSourceExecutionMode {
28 kParallel,
29 kSequenced,
30 kSingleThread,
Etienne Pierre-doray36afadeb2019-07-12 21:19:4131 kJob,
32 kMax = kJob,
Etienne Pierre-doray45fd5d52019-03-28 15:19:5533};
34
Etienne Pierre-doray312462152019-03-19 16:10:1735struct BASE_EXPORT ExecutionEnvironment {
36 SequenceToken token;
Keishi Hattorie175ac52022-06-07 06:24:5737 raw_ptr<SequenceLocalStorageMap> sequence_local_storage;
Etienne Pierre-doray312462152019-03-19 16:10:1738};
39
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3840// A TaskSource is a virtual class that provides a series of Tasks that must be
41// executed.
42//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0543// A task source is registered when it's ready to be queued. A task source is
44// ready to be queued when either:
45// 1- It has new tasks that can run concurrently as a result of external
46// operations, e.g. posting a new task to an empty Sequence or increasing
47// max concurrency of a JobTaskSource;
48// 2- A worker finished running a task from it and DidProcessTask() returned
49// true; or
50// 3- A worker is about to run a task from it and WillRunTask() returned
51// kAllowedNotSaturated.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3852//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0553// A worker may perform the following sequence of operations on a
54// RegisteredTaskSource after obtaining it from the queue:
55// 1- Check whether a task can run with WillRunTask() (and register/enqueue the
56// task source again if not saturated).
Etienne Pierre-doray388014e2019-09-13 18:40:5257// 2- (optional) Iff (1) determined that a task can run, access the next task
58// with TakeTask().
59// 3- (optional) Execute the task.
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0560// 4- Inform the task source that a task was processed with DidProcessTask(),
61// and re-enqueue the task source iff requested.
62// When a task source is registered multiple times, many overlapping chains of
63// operations may run concurrently, as permitted by WillRunTask(). This allows
64// tasks from the same task source to run in parallel.
65// However, the following invariants are kept:
66// - The number of workers concurrently running tasks never goes over the
67// intended concurrency.
68// - If the task source has more tasks that can run concurrently, it must be
69// queued.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3870//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0571// Note: there is a known refcounted-ownership cycle in the ThreadPool
Etienne Pierre-doray36afadeb2019-07-12 21:19:4172// architecture: TaskSource -> TaskRunner -> TaskSource -> ... This is okay so
73// long as the other owners of TaskSource (PriorityQueue and WorkerThread in
74// alternation and ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork()
75// temporarily) keep running it (and taking Tasks from it as a result). A
76// dangling reference cycle would only occur should they release their reference
77// to it while it's not empty. In other words, it is only correct for them to
78// release it when DidProcessTask() returns false.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3879//
80// This class is thread-safe.
81class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
82 public:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0583 // Indicates whether WillRunTask() allows TakeTask() to be called on a
84 // RegisteredTaskSource.
85 enum class RunStatus {
86 // TakeTask() cannot be called.
87 kDisallowed,
Etienne Pierre-doray388014e2019-09-13 18:40:5288 // TakeTask() may called, and the TaskSource has not reached its maximum
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0589 // concurrency (i.e. the TaskSource still needs to be queued).
90 kAllowedNotSaturated,
Etienne Pierre-doray388014e2019-09-13 18:40:5291 // TakeTask() may called, and the TaskSource has reached its maximum
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0592 // concurrency (i.e. the TaskSource no longer needs to be queued).
93 kAllowedSaturated,
Etienne Pierre-doraya57964d2019-07-08 22:04:5594 };
95
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3896 // A Transaction can perform multiple operations atomically on a
97 // TaskSource. While a Transaction is alive, it is guaranteed that nothing
98 // else will access the TaskSource; the TaskSource's lock is held for the
99 // lifetime of the Transaction.
100 class BASE_EXPORT Transaction {
101 public:
102 Transaction(Transaction&& other);
David Bienvenu85cf749b2020-10-30 15:10:59103 Transaction(const Transaction&) = delete;
104 Transaction& operator=(const Transaction&) = delete;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38105 ~Transaction();
106
Etienne Pierre-doraya57964d2019-07-08 22:04:55107 operator bool() const { return !!task_source_; }
108
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38109 // Sets TaskSource priority to |priority|.
110 void UpdatePriority(TaskPriority priority);
111
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38112 // Returns the traits of all Tasks in the TaskSource.
113 TaskTraits traits() const { return task_source_->traits_; }
114
115 TaskSource* task_source() const { return task_source_; }
116
117 protected:
118 explicit Transaction(TaskSource* task_source);
119
120 private:
121 friend class TaskSource;
122
123 TaskSource* task_source_;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38124 };
125
126 // |traits| is metadata that applies to all Tasks in the TaskSource.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55127 // |task_runner| is a reference to the TaskRunner feeding this TaskSource.
128 // |task_runner| can be nullptr only for tasks with no TaskRunner, in which
129 // case |execution_mode| must be kParallel. Otherwise, |execution_mode| is the
130 // execution mode of |task_runner|.
131 TaskSource(const TaskTraits& traits,
132 TaskRunner* task_runner,
133 TaskSourceExecutionMode execution_mode);
David Bienvenu85cf749b2020-10-30 15:10:59134 TaskSource(const TaskSource&) = delete;
135 TaskSource& operator=(const TaskSource&) = delete;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38136
137 // Begins a Transaction. This method cannot be called on a thread which has an
138 // active TaskSource::Transaction.
Daniel Cheng4455c9842022-01-13 23:26:37139 [[nodiscard]] Transaction BeginTransaction();
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38140
Etienne Pierre-doray312462152019-03-19 16:10:17141 virtual ExecutionEnvironment GetExecutionEnvironment() = 0;
142
Etienne Pierre-doray36afadeb2019-07-12 21:19:41143 // Thread-safe but the returned value may immediately be obsolete. As such
144 // this should only be used as a best-effort guess of how many more workers
Etienne Pierre-doray3bca4922019-08-13 20:56:56145 // are needed. This may be called on an empty task source.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41146 virtual size_t GetRemainingConcurrency() const = 0;
Etienne Pierre-doraya57964d2019-07-08 22:04:55147
Etienne Pierre-doray1f51cc032020-09-15 17:40:48148 // Returns a TaskSourceSortKey representing the priority of the TaskSource.
Etienne Pierre-dorayea2b21e2020-10-26 22:38:59149 virtual TaskSourceSortKey GetSortKey(bool disable_fair_scheduling) const = 0;
Etienne Pierre-doray1f51cc032020-09-15 17:40:48150
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38151 // Support for IntrusiveHeap.
152 void SetHeapHandle(const HeapHandle& handle);
153 void ClearHeapHandle();
Chris Hamilton9c9ce502019-08-22 20:53:18154 HeapHandle GetHeapHandle() const { return heap_handle_; }
155
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38156 HeapHandle heap_handle() const { return heap_handle_; }
157
158 // Returns the shutdown behavior of all Tasks in the TaskSource. Can be
159 // accessed without a Transaction because it is never mutated.
160 TaskShutdownBehavior shutdown_behavior() const {
161 return traits_.shutdown_behavior();
162 }
Etienne Pierre-dorayac3680742019-08-29 17:43:17163 // Returns a racy priority of the TaskSource. Can be accessed without a
164 // Transaction but may return an outdated result.
165 TaskPriority priority_racy() const {
166 return priority_racy_.load(std::memory_order_relaxed);
167 }
168 // Returns the thread policy of the TaskSource. Can be accessed without a
169 // Transaction because it is never mutated.
170 ThreadPolicy thread_policy() const { return traits_.thread_policy(); }
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38171
Etienne Pierre-doray45fd5d52019-03-28 15:19:55172 // A reference to TaskRunner is only retained between PushTask() and when
Etienne Pierre-doraya57964d2019-07-08 22:04:55173 // DidProcessTask() returns false, guaranteeing it is safe to dereference this
Etienne Pierre-doray45fd5d52019-03-28 15:19:55174 // pointer. Otherwise, the caller should guarantee such TaskRunner still
175 // exists before dereferencing.
176 TaskRunner* task_runner() const { return task_runner_; }
177
178 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
179
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38180 protected:
181 virtual ~TaskSource();
182
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05183 virtual RunStatus WillRunTask() = 0;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38184
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05185 // Implementations of TakeTask(), DidProcessTask() and Clear() must ensure
186 // proper synchronization iff |transaction| is nullptr.
Etienne Pierre-dorayd9901d92019-11-09 03:21:37187 virtual Task TakeTask(TaskSource::Transaction* transaction) = 0;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05188 virtual bool DidProcessTask(TaskSource::Transaction* transaction) = 0;
189
190 // This may be called for each outstanding RegisteredTaskSource that's ready.
191 // The implementation needs to support this being called multiple times;
192 // unless it guarantees never to hand-out multiple RegisteredTaskSources that
193 // are concurrently ready.
Etienne Pierre-dorayd9901d92019-11-09 03:21:37194 virtual Task Clear(TaskSource::Transaction* transaction) = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55195
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38196 // Sets TaskSource priority to |priority|.
197 void UpdatePriority(TaskPriority priority);
198
199 // The TaskTraits of all Tasks in the TaskSource.
200 TaskTraits traits_;
201
Etienne Pierre-dorayac3680742019-08-29 17:43:17202 // The cached priority for atomic access.
203 std::atomic<TaskPriority> priority_racy_;
204
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38205 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59206 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38207
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05208 private:
209 friend class RefCountedThreadSafe<TaskSource>;
210 friend class RegisteredTaskSource;
211
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38212 // The TaskSource's position in its current PriorityQueue. Access is protected
213 // by the PriorityQueue's lock.
214 HeapHandle heap_handle_;
215
Etienne Pierre-doray45fd5d52019-03-28 15:19:55216 // A pointer to the TaskRunner that posts to this TaskSource, if any. The
Etienne Pierre-dorayabfecf72019-05-09 20:41:48217 // derived class is responsible for calling AddRef() when a TaskSource from
218 // which no Task is executing becomes non-empty and Release() when
Etienne Pierre-doray388014e2019-09-13 18:40:52219 // it becomes empty again (e.g. when DidProcessTask() returns false).
Arthur Sonzognif655bbd2022-06-02 08:29:02220 //
221 // In practise, this pointer is going to become dangling. See task_runner()
222 // comment.
223 raw_ptr<TaskRunner, DisableDanglingPtrDetection> task_runner_;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55224
225 TaskSourceExecutionMode execution_mode_;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38226};
227
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05228// Wrapper around TaskSource to signify the intent to queue and run it.
229// RegisteredTaskSource can only be created with TaskTracker and may only be
230// used by a single worker at a time. However, the same task source may be
231// registered several times, spawning multiple RegisteredTaskSources. A
232// RegisteredTaskSource resets to its initial state when WillRunTask() fails
233// or after DidProcessTask(), so it can be used again.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53234class BASE_EXPORT RegisteredTaskSource {
235 public:
236 RegisteredTaskSource();
237 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55238 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
David Bienvenu85cf749b2020-10-30 15:10:59239 RegisteredTaskSource(const RegisteredTaskSource&) = delete;
240 RegisteredTaskSource& operator=(const RegisteredTaskSource&) = delete;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53241 ~RegisteredTaskSource();
242
243 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
244
245 operator bool() const { return task_source_ != nullptr; }
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53246 TaskSource* operator->() const { return task_source_.get(); }
247 TaskSource* get() const { return task_source_.get(); }
248
249 static RegisteredTaskSource CreateForTesting(
250 scoped_refptr<TaskSource> task_source,
251 TaskTracker* task_tracker = nullptr);
252
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05253 // Can only be called if this RegisteredTaskSource is in its initial state.
254 // Returns the underlying task source. An Optional is used in preparation for
255 // the merge between ThreadPool and TaskQueueManager (in Blink).
256 // https://crbug.com/783309
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53257 scoped_refptr<TaskSource> Unregister();
258
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05259 // Informs this TaskSource that the current worker would like to run a Task
260 // from it. Can only be called if in its initial state. Returns a RunStatus
261 // that indicates if the operation is allowed (TakeTask() can be called).
262 TaskSource::RunStatus WillRunTask();
263
264 // Returns the next task to run from this TaskSource. This should be called
265 // only after WillRunTask() returned RunStatus::kAllowed*. |transaction| is
266 // optional and should only be provided if this operation is already part of
267 // a transaction.
Daniel Cheng4455c9842022-01-13 23:26:37268 [[nodiscard]] Task TakeTask(TaskSource::Transaction* transaction = nullptr);
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05269
Etienne Pierre-doray388014e2019-09-13 18:40:52270 // Must be called after WillRunTask() or once the task was run if TakeTask()
271 // was called. This resets this RegisteredTaskSource to its initial state so
272 // that WillRunTask() may be called again. |transaction| is optional and
273 // should only be provided if this operation is already part of a transaction.
274 // Returns true if the TaskSource should be queued after this operation.
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05275 bool DidProcessTask(TaskSource::Transaction* transaction = nullptr);
276
277 // Returns a task that clears this TaskSource to make it empty. |transaction|
278 // is optional and should only be provided if this operation is already part
279 // of a transaction.
Daniel Cheng4455c9842022-01-13 23:26:37280 [[nodiscard]] Task Clear(TaskSource::Transaction* transaction = nullptr);
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05281
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53282 private:
283 friend class TaskTracker;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53284 RegisteredTaskSource(scoped_refptr<TaskSource> task_source,
285 TaskTracker* task_tracker);
286
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05287#if DCHECK_IS_ON()
288 // Indicates the step of a task execution chain.
289 enum class State {
290 kInitial, // WillRunTask() may be called.
291 kReady, // After WillRunTask() returned a valid RunStatus.
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05292 };
293
294 State run_step_ = State::kInitial;
295#endif // DCHECK_IS_ON()
296
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53297 scoped_refptr<TaskSource> task_source_;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05298 TaskTracker* task_tracker_ = nullptr;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38299};
300
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05301// A pair of Transaction and RegisteredTaskSource. Useful to carry a
302// RegisteredTaskSource with an associated Transaction.
303// TODO(crbug.com/839091): Rename to RegisteredTaskSourceAndTransaction.
304struct BASE_EXPORT TransactionWithRegisteredTaskSource {
Etienne Pierre-doraya57964d2019-07-08 22:04:55305 public:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05306 TransactionWithRegisteredTaskSource(RegisteredTaskSource task_source_in,
307 TaskSource::Transaction transaction_in);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53308
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05309 TransactionWithRegisteredTaskSource(
310 TransactionWithRegisteredTaskSource&& other) = default;
David Bienvenu85cf749b2020-10-30 15:10:59311 TransactionWithRegisteredTaskSource(
312 const TransactionWithRegisteredTaskSource&) = delete;
313 TransactionWithRegisteredTaskSource& operator=(
314 const TransactionWithRegisteredTaskSource&) = delete;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05315 ~TransactionWithRegisteredTaskSource() = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53316
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05317 static TransactionWithRegisteredTaskSource FromTaskSource(
318 RegisteredTaskSource task_source_in);
Etienne Pierre-doraya57964d2019-07-08 22:04:55319
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05320 RegisteredTaskSource task_source;
321 TaskSource::Transaction transaction;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53322};
323
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38324} // namespace internal
325} // namespace base
326
Gabriel Charette52fa3ae2019-04-15 21:44:37327#endif // BASE_TASK_THREAD_POOL_TASK_SOURCE_H_