blob: df8bebe98e2c5c1e3eca1a4fadfe2dbb8ec3625d [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"
Etienne Pierre-dorayf7f59c32019-05-24 16:50:5311#include "base/compiler_specific.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3812#include "base/macros.h"
13#include "base/memory/ref_counted.h"
14#include "base/optional.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/common/intrusive_heap.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3818#include "base/task/task_traits.h"
Gabriel Charette52fa3ae2019-04-15 21:44:3719#include "base/task/thread_pool/sequence_sort_key.h"
20#include "base/task/thread_pool/task.h"
Etienne Pierre-doray312462152019-03-19 16:10:1721#include "base/threading/sequence_local_storage_map.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3822
23namespace base {
24namespace internal {
25
Etienne Pierre-dorayf7f59c32019-05-24 16:50:5326class TaskTracker;
27
Etienne Pierre-doray45fd5d52019-03-28 15:19:5528enum class TaskSourceExecutionMode {
29 kParallel,
30 kSequenced,
31 kSingleThread,
Etienne Pierre-doray36afadeb2019-07-12 21:19:4132 kJob,
33 kMax = kJob,
Etienne Pierre-doray45fd5d52019-03-28 15:19:5534};
35
Etienne Pierre-doray312462152019-03-19 16:10:1736struct BASE_EXPORT ExecutionEnvironment {
37 SequenceToken token;
38 SequenceLocalStorageMap* sequence_local_storage;
39};
40
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3841// A TaskSource is a virtual class that provides a series of Tasks that must be
42// executed.
43//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0544// A task source is registered when it's ready to be queued. A task source is
45// ready to be queued when either:
46// 1- It has new tasks that can run concurrently as a result of external
47// operations, e.g. posting a new task to an empty Sequence or increasing
48// max concurrency of a JobTaskSource;
49// 2- A worker finished running a task from it and DidProcessTask() returned
50// true; or
51// 3- A worker is about to run a task from it and WillRunTask() returned
52// kAllowedNotSaturated.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3853//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0554// A worker may perform the following sequence of operations on a
55// RegisteredTaskSource after obtaining it from the queue:
56// 1- Check whether a task can run with WillRunTask() (and register/enqueue the
57// task source again if not saturated).
58// 2- Iff (1) determined that a task can run, access the next task with
59// TakeTask().
60// 3- Execute the task.
61// 4- Inform the task source that a task was processed with DidProcessTask(),
62// and re-enqueue the task source iff requested.
63// When a task source is registered multiple times, many overlapping chains of
64// operations may run concurrently, as permitted by WillRunTask(). This allows
65// tasks from the same task source to run in parallel.
66// However, the following invariants are kept:
67// - The number of workers concurrently running tasks never goes over the
68// intended concurrency.
69// - If the task source has more tasks that can run concurrently, it must be
70// queued.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3871//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0572// Note: there is a known refcounted-ownership cycle in the ThreadPool
Etienne Pierre-doray36afadeb2019-07-12 21:19:4173// architecture: TaskSource -> TaskRunner -> TaskSource -> ... This is okay so
74// long as the other owners of TaskSource (PriorityQueue and WorkerThread in
75// alternation and ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork()
76// temporarily) keep running it (and taking Tasks from it as a result). A
77// dangling reference cycle would only occur should they release their reference
78// to it while it's not empty. In other words, it is only correct for them to
79// release it when DidProcessTask() returns false.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3880//
81// This class is thread-safe.
82class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3883 public:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0584 // Indicates whether WillRunTask() allows TakeTask() to be called on a
85 // RegisteredTaskSource.
86 enum class RunStatus {
87 // TakeTask() cannot be called.
88 kDisallowed,
89 // TakeTask() must be called, and the TaskSource has not reached its maximum
90 // concurrency (i.e. the TaskSource still needs to be queued).
91 kAllowedNotSaturated,
92 // TakeTask() must be called, and the TaskSource has reached its maximum
93 // concurrency (i.e. the TaskSource no longer needs to be queued).
94 kAllowedSaturated,
Etienne Pierre-doraya57964d2019-07-08 22:04:5595 };
96
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3897 // A Transaction can perform multiple operations atomically on a
98 // TaskSource. While a Transaction is alive, it is guaranteed that nothing
99 // else will access the TaskSource; the TaskSource's lock is held for the
100 // lifetime of the Transaction.
101 class BASE_EXPORT Transaction {
102 public:
103 Transaction(Transaction&& other);
104 ~Transaction();
105
Etienne Pierre-doraya57964d2019-07-08 22:04:55106 operator bool() const { return !!task_source_; }
107
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38108 // Returns a SequenceSortKey representing the priority of the TaskSource.
109 // Cannot be called on an empty TaskSource.
110 SequenceSortKey GetSortKey() const;
111
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38112 // Sets TaskSource priority to |priority|.
113 void UpdatePriority(TaskPriority priority);
114
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38115 // Returns the traits of all Tasks in the TaskSource.
116 TaskTraits traits() const { return task_source_->traits_; }
117
118 TaskSource* task_source() const { return task_source_; }
119
120 protected:
121 explicit Transaction(TaskSource* task_source);
122
123 private:
124 friend class TaskSource;
125
126 TaskSource* task_source_;
127
128 DISALLOW_COPY_AND_ASSIGN(Transaction);
129 };
130
131 // |traits| is metadata that applies to all Tasks in the TaskSource.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55132 // |task_runner| is a reference to the TaskRunner feeding this TaskSource.
133 // |task_runner| can be nullptr only for tasks with no TaskRunner, in which
134 // case |execution_mode| must be kParallel. Otherwise, |execution_mode| is the
135 // execution mode of |task_runner|.
136 TaskSource(const TaskTraits& traits,
137 TaskRunner* task_runner,
138 TaskSourceExecutionMode execution_mode);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38139
140 // Begins a Transaction. This method cannot be called on a thread which has an
141 // active TaskSource::Transaction.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53142 Transaction BeginTransaction() WARN_UNUSED_RESULT;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38143
Etienne Pierre-doray312462152019-03-19 16:10:17144 virtual ExecutionEnvironment GetExecutionEnvironment() = 0;
145
Etienne Pierre-doray36afadeb2019-07-12 21:19:41146 // Thread-safe but the returned value may immediately be obsolete. As such
147 // this should only be used as a best-effort guess of how many more workers
Etienne Pierre-doray3bca4922019-08-13 20:56:56148 // are needed. This may be called on an empty task source.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41149 virtual size_t GetRemainingConcurrency() const = 0;
Etienne Pierre-doraya57964d2019-07-08 22:04:55150
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 }
163
Etienne Pierre-doray45fd5d52019-03-28 15:19:55164 // A reference to TaskRunner is only retained between PushTask() and when
Etienne Pierre-doraya57964d2019-07-08 22:04:55165 // DidProcessTask() returns false, guaranteeing it is safe to dereference this
Etienne Pierre-doray45fd5d52019-03-28 15:19:55166 // pointer. Otherwise, the caller should guarantee such TaskRunner still
167 // exists before dereferencing.
168 TaskRunner* task_runner() const { return task_runner_; }
169
170 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
171
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38172 protected:
173 virtual ~TaskSource();
174
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05175 virtual RunStatus WillRunTask() = 0;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38176
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05177 // Implementations of TakeTask(), DidProcessTask() and Clear() must ensure
178 // proper synchronization iff |transaction| is nullptr.
179 virtual Optional<Task> TakeTask(TaskSource::Transaction* transaction) = 0;
180 virtual bool DidProcessTask(TaskSource::Transaction* transaction) = 0;
181
182 // This may be called for each outstanding RegisteredTaskSource that's ready.
183 // The implementation needs to support this being called multiple times;
184 // unless it guarantees never to hand-out multiple RegisteredTaskSources that
185 // are concurrently ready.
186 virtual Optional<Task> Clear(TaskSource::Transaction* transaction) = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55187
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38188 virtual SequenceSortKey GetSortKey() const = 0;
189
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38190 // Sets TaskSource priority to |priority|.
191 void UpdatePriority(TaskPriority priority);
192
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38193 // The TaskTraits of all Tasks in the TaskSource.
194 TaskTraits traits_;
195
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38196 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59197 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38198
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05199 private:
200 friend class RefCountedThreadSafe<TaskSource>;
201 friend class RegisteredTaskSource;
202
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38203 // The TaskSource's position in its current PriorityQueue. Access is protected
204 // by the PriorityQueue's lock.
205 HeapHandle heap_handle_;
206
Etienne Pierre-doray45fd5d52019-03-28 15:19:55207 // A pointer to the TaskRunner that posts to this TaskSource, if any. The
Etienne Pierre-dorayabfecf72019-05-09 20:41:48208 // derived class is responsible for calling AddRef() when a TaskSource from
209 // which no Task is executing becomes non-empty and Release() when
Etienne Pierre-doraya57964d2019-07-08 22:04:55210 // DidProcessTask() returns false.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55211 TaskRunner* task_runner_;
212
213 TaskSourceExecutionMode execution_mode_;
214
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38215 DISALLOW_COPY_AND_ASSIGN(TaskSource);
216};
217
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05218// Wrapper around TaskSource to signify the intent to queue and run it.
219// RegisteredTaskSource can only be created with TaskTracker and may only be
220// used by a single worker at a time. However, the same task source may be
221// registered several times, spawning multiple RegisteredTaskSources. A
222// RegisteredTaskSource resets to its initial state when WillRunTask() fails
223// or after DidProcessTask(), so it can be used again.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53224class BASE_EXPORT RegisteredTaskSource {
225 public:
226 RegisteredTaskSource();
227 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55228 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53229 ~RegisteredTaskSource();
230
231 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
232
233 operator bool() const { return task_source_ != nullptr; }
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53234 TaskSource* operator->() const { return task_source_.get(); }
235 TaskSource* get() const { return task_source_.get(); }
236
237 static RegisteredTaskSource CreateForTesting(
238 scoped_refptr<TaskSource> task_source,
239 TaskTracker* task_tracker = nullptr);
240
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05241 // Can only be called if this RegisteredTaskSource is in its initial state.
242 // Returns the underlying task source. An Optional is used in preparation for
243 // the merge between ThreadPool and TaskQueueManager (in Blink).
244 // https://crbug.com/783309
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53245 scoped_refptr<TaskSource> Unregister();
246
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05247 // Informs this TaskSource that the current worker would like to run a Task
248 // from it. Can only be called if in its initial state. Returns a RunStatus
249 // that indicates if the operation is allowed (TakeTask() can be called).
250 TaskSource::RunStatus WillRunTask();
251
252 // Returns the next task to run from this TaskSource. This should be called
253 // only after WillRunTask() returned RunStatus::kAllowed*. |transaction| is
254 // optional and should only be provided if this operation is already part of
255 // a transaction.
256 //
257 // Because this method cannot be called on an empty TaskSource, the returned
258 // Optional<Task> is never nullopt.
259 Optional<Task> TakeTask(TaskSource::Transaction* transaction = nullptr)
260 WARN_UNUSED_RESULT;
261
262 // Must be called once the task was run. This resets this RegisteredTaskSource
263 // to its initial state so that WillRunTask() may be called again.
264 // |transaction| is optional and should only be provided if this operation is
265 // already part of a transaction. Returns true if the TaskSource should be
266 // queued after this operation.
267 bool DidProcessTask(TaskSource::Transaction* transaction = nullptr);
268
269 // Returns a task that clears this TaskSource to make it empty. |transaction|
270 // is optional and should only be provided if this operation is already part
271 // of a transaction.
272 Optional<Task> Clear(TaskSource::Transaction* transaction = nullptr)
273 WARN_UNUSED_RESULT;
274
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53275 private:
276 friend class TaskTracker;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53277 RegisteredTaskSource(scoped_refptr<TaskSource> task_source,
278 TaskTracker* task_tracker);
279
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05280#if DCHECK_IS_ON()
281 // Indicates the step of a task execution chain.
282 enum class State {
283 kInitial, // WillRunTask() may be called.
284 kReady, // After WillRunTask() returned a valid RunStatus.
285 kTaskAcquired, // After TakeTask().
286 };
287
288 State run_step_ = State::kInitial;
289#endif // DCHECK_IS_ON()
290
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53291 scoped_refptr<TaskSource> task_source_;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05292 TaskTracker* task_tracker_ = nullptr;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53293
294 DISALLOW_COPY_AND_ASSIGN(RegisteredTaskSource);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38295};
296
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05297// A pair of Transaction and RegisteredTaskSource. Useful to carry a
298// RegisteredTaskSource with an associated Transaction.
299// TODO(crbug.com/839091): Rename to RegisteredTaskSourceAndTransaction.
300struct BASE_EXPORT TransactionWithRegisteredTaskSource {
Etienne Pierre-doraya57964d2019-07-08 22:04:55301 public:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05302 TransactionWithRegisteredTaskSource(RegisteredTaskSource task_source_in,
303 TaskSource::Transaction transaction_in);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53304
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05305 TransactionWithRegisteredTaskSource(
306 TransactionWithRegisteredTaskSource&& other) = default;
307 ~TransactionWithRegisteredTaskSource() = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53308
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05309 static TransactionWithRegisteredTaskSource FromTaskSource(
310 RegisteredTaskSource task_source_in);
Etienne Pierre-doraya57964d2019-07-08 22:04:55311
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05312 RegisteredTaskSource task_source;
313 TaskSource::Transaction transaction;
Etienne Pierre-doraya57964d2019-07-08 22:04:55314
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05315 DISALLOW_COPY_AND_ASSIGN(TransactionWithRegisteredTaskSource);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53316};
317
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38318} // namespace internal
319} // namespace base
320
Gabriel Charette52fa3ae2019-04-15 21:44:37321#endif // BASE_TASK_THREAD_POOL_TASK_SOURCE_H_