blob: 391b1d65f6c1774f0f5bd3be01080dc233042eea [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-doraya57964d2019-07-08 22:04:5544// In order to execute a task from this TaskSource, a worker should first make
Etienne Pierre-doray36afadeb2019-07-12 21:19:4145// sure that a task can run with WillRunTask() which returns a RunIntent.
46// TakeTask() can then be called to access the next Task, and DidProcessTask()
47// must be called after the task was processed. Many overlapping chains of
48// WillRunTask(), TakeTask(), run and DidProcessTask() can run concurrently, as
49// permitted by WillRunTask(). This ensure that the number of workers
50// concurrently running tasks never go over the intended concurrency.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3851//
52// In comments below, an "empty TaskSource" is a TaskSource with no Task.
53//
54// Note: there is a known refcounted-ownership cycle in the Scheduler
Etienne Pierre-doray36afadeb2019-07-12 21:19:4155// architecture: TaskSource -> TaskRunner -> TaskSource -> ... This is okay so
56// long as the other owners of TaskSource (PriorityQueue and WorkerThread in
57// alternation and ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork()
58// temporarily) keep running it (and taking Tasks from it as a result). A
59// dangling reference cycle would only occur should they release their reference
60// to it while it's not empty. In other words, it is only correct for them to
61// release it when DidProcessTask() returns false.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3862//
63// This class is thread-safe.
64class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
Etienne Pierre-doray36afadeb2019-07-12 21:19:4165 protected:
66 // Indicates whether a TaskSource has reached its maximum intended concurrency
67 // and may not run any additional tasks.
68 enum class Saturated {
69 kYes,
70 kNo,
71 };
72
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3873 public:
Etienne Pierre-doraya57964d2019-07-08 22:04:5574 // Result of WillRunTask(). A single task associated with a RunIntent may be
Etienne Pierre-doray3bca4922019-08-13 20:56:5675 // accessed with TakeTask() and run, or the task source may be cleared with
76 // Clear() iff this evaluates to true.
Etienne Pierre-doraya57964d2019-07-08 22:04:5577 class BASE_EXPORT RunIntent {
78 public:
79 RunIntent() = default;
80 RunIntent(RunIntent&&) noexcept;
81 ~RunIntent();
82
83 RunIntent& operator=(RunIntent&&);
84
85 operator bool() const { return !!task_source_; }
86
87 // Returns true iff the TaskSource from which this RunIntent was obtained
88 // may not run any additional tasks beyond this RunIntent as it has reached
89 // its maximum concurrency. This indicates that the TaskSource no longer
90 // needs to be queued.
Etienne Pierre-doray36afadeb2019-07-12 21:19:4191 bool IsSaturated() const { return is_saturated_ == Saturated::kYes; }
Etienne Pierre-doraya57964d2019-07-08 22:04:5592
93 const TaskSource* task_source() const { return task_source_; }
94
Etienne Pierre-doray36afadeb2019-07-12 21:19:4195 void ReleaseForTesting() {
96 DCHECK(task_source_);
97 task_source_ = nullptr;
98 }
99
Etienne Pierre-doraya57964d2019-07-08 22:04:55100 private:
101 friend class TaskSource;
102
Etienne Pierre-doray36afadeb2019-07-12 21:19:41103 // Indicates the step of a run intent chain.
104 enum class State {
105 kInitial, // After WillRunTask().
106 kTaskAcquired, // After TakeTask().
107 kCompleted, // After DidProcessTask().
108 };
109
110 RunIntent(const TaskSource* task_source, Saturated is_saturated);
Etienne Pierre-doraya57964d2019-07-08 22:04:55111
112 void Release() {
Etienne Pierre-doray36afadeb2019-07-12 21:19:41113 DCHECK_EQ(run_step_, State::kCompleted);
Etienne Pierre-doraya57964d2019-07-08 22:04:55114 DCHECK(task_source_);
115 task_source_ = nullptr;
116 }
117
118 const TaskSource* task_source_ = nullptr;
Etienne Pierre-doray36afadeb2019-07-12 21:19:41119 State run_step_ = State::kInitial;
120 Saturated is_saturated_ = Saturated::kYes;
Etienne Pierre-doraya57964d2019-07-08 22:04:55121 };
122
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38123 // A Transaction can perform multiple operations atomically on a
124 // TaskSource. While a Transaction is alive, it is guaranteed that nothing
125 // else will access the TaskSource; the TaskSource's lock is held for the
126 // lifetime of the Transaction.
127 class BASE_EXPORT Transaction {
128 public:
129 Transaction(Transaction&& other);
130 ~Transaction();
131
Etienne Pierre-doraya57964d2019-07-08 22:04:55132 operator bool() const { return !!task_source_; }
133
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38134 // Returns the next task to run from this TaskSource. This should be called
Etienne Pierre-doray36afadeb2019-07-12 21:19:41135 // only with a valid |intent|. Cannot be called on an empty TaskSource.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38136 //
137 // Because this method cannot be called on an empty TaskSource, the returned
138 // Optional<Task> is never nullptr. An Optional is used in preparation for
Gabriel Charette52fa3ae2019-04-15 21:44:37139 // the merge between ThreadPool and TaskQueueManager (in Blink).
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38140 // https://crbug.com/783309
Etienne Pierre-doray36afadeb2019-07-12 21:19:41141 Optional<Task> TakeTask(RunIntent* intent) WARN_UNUSED_RESULT;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38142
Etienne Pierre-doray3bca4922019-08-13 20:56:56143 // Returns a task that clears this TaskSource to make it empty. This should
144 // be called only with a valid |intent|, but may be called for each valid
145 // outstanding RunIntent.
146 Optional<Task> Clear(RunIntent intent) WARN_UNUSED_RESULT;
147
148 // Must be called once the task was run. Cannot be called on an empty
149 // TaskSource. Returns true if the TaskSource should be queued after this
150 // operation.
151 bool DidProcessTask(RunIntent intent);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38152
153 // Returns a SequenceSortKey representing the priority of the TaskSource.
154 // Cannot be called on an empty TaskSource.
155 SequenceSortKey GetSortKey() const;
156
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38157 // Sets TaskSource priority to |priority|.
158 void UpdatePriority(TaskPriority priority);
159
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38160 // Returns the traits of all Tasks in the TaskSource.
161 TaskTraits traits() const { return task_source_->traits_; }
162
163 TaskSource* task_source() const { return task_source_; }
164
165 protected:
166 explicit Transaction(TaskSource* task_source);
167
168 private:
169 friend class TaskSource;
170
171 TaskSource* task_source_;
172
173 DISALLOW_COPY_AND_ASSIGN(Transaction);
174 };
175
176 // |traits| is metadata that applies to all Tasks in the TaskSource.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55177 // |task_runner| is a reference to the TaskRunner feeding this TaskSource.
178 // |task_runner| can be nullptr only for tasks with no TaskRunner, in which
179 // case |execution_mode| must be kParallel. Otherwise, |execution_mode| is the
180 // execution mode of |task_runner|.
181 TaskSource(const TaskTraits& traits,
182 TaskRunner* task_runner,
183 TaskSourceExecutionMode execution_mode);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38184
185 // Begins a Transaction. This method cannot be called on a thread which has an
186 // active TaskSource::Transaction.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53187 Transaction BeginTransaction() WARN_UNUSED_RESULT;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38188
Etienne Pierre-doray312462152019-03-19 16:10:17189 virtual ExecutionEnvironment GetExecutionEnvironment() = 0;
190
Etienne Pierre-doraya57964d2019-07-08 22:04:55191 // Informs this TaskSource that an additional Task could be run. Returns a
192 // RunIntent that evaluates to true if this operation is allowed (TakeTask()
Etienne Pierre-doray3bca4922019-08-13 20:56:56193 // or Clear() can be called), or false otherwise.
Etienne Pierre-doraya57964d2019-07-08 22:04:55194 virtual RunIntent WillRunTask() = 0;
195
Etienne Pierre-doray36afadeb2019-07-12 21:19:41196 // Thread-safe but the returned value may immediately be obsolete. As such
197 // this should only be used as a best-effort guess of how many more workers
Etienne Pierre-doray3bca4922019-08-13 20:56:56198 // are needed. This may be called on an empty task source.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41199 virtual size_t GetRemainingConcurrency() const = 0;
Etienne Pierre-doraya57964d2019-07-08 22:04:55200
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38201 // Support for IntrusiveHeap.
202 void SetHeapHandle(const HeapHandle& handle);
203 void ClearHeapHandle();
Chris Hamilton9c9ce502019-08-22 20:53:18204 HeapHandle GetHeapHandle() const { return heap_handle_; }
205
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38206 HeapHandle heap_handle() const { return heap_handle_; }
207
208 // Returns the shutdown behavior of all Tasks in the TaskSource. Can be
209 // accessed without a Transaction because it is never mutated.
210 TaskShutdownBehavior shutdown_behavior() const {
211 return traits_.shutdown_behavior();
212 }
213
Etienne Pierre-doray45fd5d52019-03-28 15:19:55214 // A reference to TaskRunner is only retained between PushTask() and when
Etienne Pierre-doraya57964d2019-07-08 22:04:55215 // DidProcessTask() returns false, guaranteeing it is safe to dereference this
Etienne Pierre-doray45fd5d52019-03-28 15:19:55216 // pointer. Otherwise, the caller should guarantee such TaskRunner still
217 // exists before dereferencing.
218 TaskRunner* task_runner() const { return task_runner_; }
219
220 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
221
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38222 protected:
223 virtual ~TaskSource();
224
225 virtual Optional<Task> TakeTask() = 0;
226
Etienne Pierre-doraya57964d2019-07-08 22:04:55227 // Informs this TaskSource that a task was processed. |was_run| indicates
Etienne Pierre-doray36afadeb2019-07-12 21:19:41228 // whether the task executed or not. Returns true if the TaskSource
Etienne Pierre-doraya57964d2019-07-08 22:04:55229 // should be queued after this operation.
Etienne Pierre-doray3bca4922019-08-13 20:56:56230 virtual bool DidProcessTask() = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55231
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38232 virtual SequenceSortKey GetSortKey() const = 0;
233
Etienne Pierre-doray3bca4922019-08-13 20:56:56234 // This may be called for each outstanding RunIntent. If applicable, the
235 // implementation needs to support this being called multiple times.
236 virtual Optional<Task> Clear() = 0;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38237
238 // Sets TaskSource priority to |priority|.
239 void UpdatePriority(TaskPriority priority);
240
Etienne Pierre-doraya57964d2019-07-08 22:04:55241 // Constructs and returns a RunIntent, where |is_saturated| indicates that the
242 // TaskSource has reached its maximum concurrency.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41243 RunIntent MakeRunIntent(Saturated is_saturated) const;
Etienne Pierre-doraya57964d2019-07-08 22:04:55244
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38245 // The TaskTraits of all Tasks in the TaskSource.
246 TaskTraits traits_;
247
248 private:
249 friend class RefCountedThreadSafe<TaskSource>;
250
251 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59252 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38253
254 // The TaskSource's position in its current PriorityQueue. Access is protected
255 // by the PriorityQueue's lock.
256 HeapHandle heap_handle_;
257
Etienne Pierre-doray45fd5d52019-03-28 15:19:55258 // A pointer to the TaskRunner that posts to this TaskSource, if any. The
Etienne Pierre-dorayabfecf72019-05-09 20:41:48259 // derived class is responsible for calling AddRef() when a TaskSource from
260 // which no Task is executing becomes non-empty and Release() when
Etienne Pierre-doraya57964d2019-07-08 22:04:55261 // DidProcessTask() returns false.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55262 TaskRunner* task_runner_;
263
264 TaskSourceExecutionMode execution_mode_;
265
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38266 DISALLOW_COPY_AND_ASSIGN(TaskSource);
267};
268
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53269// Wrapper around TaskSource to signify the intent to queue and run it. A
270// RegisteredTaskSource can only be created with TaskTracker.
271class BASE_EXPORT RegisteredTaskSource {
272 public:
273 RegisteredTaskSource();
274 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55275 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53276 ~RegisteredTaskSource();
277
278 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
279
280 operator bool() const { return task_source_ != nullptr; }
281
282 TaskSource* operator->() const { return task_source_.get(); }
283 TaskSource* get() const { return task_source_.get(); }
284
285 static RegisteredTaskSource CreateForTesting(
286 scoped_refptr<TaskSource> task_source,
287 TaskTracker* task_tracker = nullptr);
288
289 scoped_refptr<TaskSource> Unregister();
290
291 private:
292 friend class TaskTracker;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53293 RegisteredTaskSource(scoped_refptr<TaskSource> task_source,
294 TaskTracker* task_tracker);
295
296 scoped_refptr<TaskSource> task_source_;
297 TaskTracker* task_tracker_;
298
299 DISALLOW_COPY_AND_ASSIGN(RegisteredTaskSource);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38300};
301
Etienne Pierre-doraya57964d2019-07-08 22:04:55302// Base implementation for TransactionWith[Owned/Registered]TaskSource (with
303// Transaction as the decorator) and RunIntentWithRegisteredTaskSource (with
304// RunIntent as the decorator).
305template <class Decorator, class T>
306class BASE_EXPORT DecoratorWithTaskSource : public Decorator {
307 public:
308 DecoratorWithTaskSource() = default;
309 DecoratorWithTaskSource(std::nullptr_t) : DecoratorWithTaskSource() {}
310 DecoratorWithTaskSource(T task_source_in, Decorator decorator)
311 : Decorator(std::move(decorator)),
312 task_source_(std::move(task_source_in)) {
313 DCHECK_EQ(task_source_.get(), this->task_source());
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53314 }
Etienne Pierre-doraya57964d2019-07-08 22:04:55315 DecoratorWithTaskSource(DecoratorWithTaskSource&& other) = default;
316 ~DecoratorWithTaskSource() = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53317
Etienne Pierre-doraya57964d2019-07-08 22:04:55318 DecoratorWithTaskSource& operator=(DecoratorWithTaskSource&&) = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53319
Etienne Pierre-doraya57964d2019-07-08 22:04:55320 T take_task_source() { return std::move(task_source_); }
321
322 protected:
323 T task_source_;
324
325 DISALLOW_COPY_AND_ASSIGN(DecoratorWithTaskSource);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53326};
327
Etienne Pierre-doraya57964d2019-07-08 22:04:55328// A RunIntent with an additional RegisteredTaskSource member.
329using RunIntentWithRegisteredTaskSource =
330 DecoratorWithTaskSource<TaskSource::RunIntent, RegisteredTaskSource>;
331
332template <class T>
333struct BASE_EXPORT BasicTransactionWithTaskSource
334 : public DecoratorWithTaskSource<TaskSource::Transaction, T> {
335 using DecoratorWithTaskSource<TaskSource::Transaction,
336 T>::DecoratorWithTaskSource;
337
338 static BasicTransactionWithTaskSource FromTaskSource(T task_source) {
339 auto transaction = task_source->BeginTransaction();
340 return BasicTransactionWithTaskSource(std::move(task_source),
341 std::move(transaction));
342 }
343};
344
345// A Transaction with an additional scoped_refptr<TaskSource> member. Useful to
346// carry ownership of a TaskSource with an associated Transaction.
347using TransactionWithOwnedTaskSource =
348 BasicTransactionWithTaskSource<scoped_refptr<TaskSource>>;
349
350// A Transaction with an additional RegisteredTaskSource member. Useful to carry
351// a RegisteredTaskSource with an associated Transaction.
352using TransactionWithRegisteredTaskSource =
353 BasicTransactionWithTaskSource<RegisteredTaskSource>;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53354
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38355} // namespace internal
356} // namespace base
357
Gabriel Charette52fa3ae2019-04-15 21:44:37358#endif // BASE_TASK_THREAD_POOL_TASK_SOURCE_H_