blob: df28aea2297f7e2bf94da242e4f0a124dbd4982b [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();
204 HeapHandle heap_handle() const { return heap_handle_; }
205
206 // Returns the shutdown behavior of all Tasks in the TaskSource. Can be
207 // accessed without a Transaction because it is never mutated.
208 TaskShutdownBehavior shutdown_behavior() const {
209 return traits_.shutdown_behavior();
210 }
211
Etienne Pierre-doray45fd5d52019-03-28 15:19:55212 // A reference to TaskRunner is only retained between PushTask() and when
Etienne Pierre-doraya57964d2019-07-08 22:04:55213 // DidProcessTask() returns false, guaranteeing it is safe to dereference this
Etienne Pierre-doray45fd5d52019-03-28 15:19:55214 // pointer. Otherwise, the caller should guarantee such TaskRunner still
215 // exists before dereferencing.
216 TaskRunner* task_runner() const { return task_runner_; }
217
218 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
219
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38220 protected:
221 virtual ~TaskSource();
222
223 virtual Optional<Task> TakeTask() = 0;
224
Etienne Pierre-doraya57964d2019-07-08 22:04:55225 // Informs this TaskSource that a task was processed. |was_run| indicates
Etienne Pierre-doray36afadeb2019-07-12 21:19:41226 // whether the task executed or not. Returns true if the TaskSource
Etienne Pierre-doraya57964d2019-07-08 22:04:55227 // should be queued after this operation.
Etienne Pierre-doray3bca4922019-08-13 20:56:56228 virtual bool DidProcessTask() = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55229
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38230 virtual SequenceSortKey GetSortKey() const = 0;
231
Etienne Pierre-doray3bca4922019-08-13 20:56:56232 // This may be called for each outstanding RunIntent. If applicable, the
233 // implementation needs to support this being called multiple times.
234 virtual Optional<Task> Clear() = 0;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38235
236 // Sets TaskSource priority to |priority|.
237 void UpdatePriority(TaskPriority priority);
238
Etienne Pierre-doraya57964d2019-07-08 22:04:55239 // Constructs and returns a RunIntent, where |is_saturated| indicates that the
240 // TaskSource has reached its maximum concurrency.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41241 RunIntent MakeRunIntent(Saturated is_saturated) const;
Etienne Pierre-doraya57964d2019-07-08 22:04:55242
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38243 // The TaskTraits of all Tasks in the TaskSource.
244 TaskTraits traits_;
245
246 private:
247 friend class RefCountedThreadSafe<TaskSource>;
248
249 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59250 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38251
252 // The TaskSource's position in its current PriorityQueue. Access is protected
253 // by the PriorityQueue's lock.
254 HeapHandle heap_handle_;
255
Etienne Pierre-doray45fd5d52019-03-28 15:19:55256 // A pointer to the TaskRunner that posts to this TaskSource, if any. The
Etienne Pierre-dorayabfecf72019-05-09 20:41:48257 // derived class is responsible for calling AddRef() when a TaskSource from
258 // which no Task is executing becomes non-empty and Release() when
Etienne Pierre-doraya57964d2019-07-08 22:04:55259 // DidProcessTask() returns false.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55260 TaskRunner* task_runner_;
261
262 TaskSourceExecutionMode execution_mode_;
263
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38264 DISALLOW_COPY_AND_ASSIGN(TaskSource);
265};
266
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53267// Wrapper around TaskSource to signify the intent to queue and run it. A
268// RegisteredTaskSource can only be created with TaskTracker.
269class BASE_EXPORT RegisteredTaskSource {
270 public:
271 RegisteredTaskSource();
272 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55273 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53274 ~RegisteredTaskSource();
275
276 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
277
278 operator bool() const { return task_source_ != nullptr; }
279
280 TaskSource* operator->() const { return task_source_.get(); }
281 TaskSource* get() const { return task_source_.get(); }
282
283 static RegisteredTaskSource CreateForTesting(
284 scoped_refptr<TaskSource> task_source,
285 TaskTracker* task_tracker = nullptr);
286
287 scoped_refptr<TaskSource> Unregister();
288
289 private:
290 friend class TaskTracker;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53291 RegisteredTaskSource(scoped_refptr<TaskSource> task_source,
292 TaskTracker* task_tracker);
293
294 scoped_refptr<TaskSource> task_source_;
295 TaskTracker* task_tracker_;
296
297 DISALLOW_COPY_AND_ASSIGN(RegisteredTaskSource);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38298};
299
Etienne Pierre-doraya57964d2019-07-08 22:04:55300// Base implementation for TransactionWith[Owned/Registered]TaskSource (with
301// Transaction as the decorator) and RunIntentWithRegisteredTaskSource (with
302// RunIntent as the decorator).
303template <class Decorator, class T>
304class BASE_EXPORT DecoratorWithTaskSource : public Decorator {
305 public:
306 DecoratorWithTaskSource() = default;
307 DecoratorWithTaskSource(std::nullptr_t) : DecoratorWithTaskSource() {}
308 DecoratorWithTaskSource(T task_source_in, Decorator decorator)
309 : Decorator(std::move(decorator)),
310 task_source_(std::move(task_source_in)) {
311 DCHECK_EQ(task_source_.get(), this->task_source());
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53312 }
Etienne Pierre-doraya57964d2019-07-08 22:04:55313 DecoratorWithTaskSource(DecoratorWithTaskSource&& other) = default;
314 ~DecoratorWithTaskSource() = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53315
Etienne Pierre-doraya57964d2019-07-08 22:04:55316 DecoratorWithTaskSource& operator=(DecoratorWithTaskSource&&) = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53317
Etienne Pierre-doraya57964d2019-07-08 22:04:55318 T take_task_source() { return std::move(task_source_); }
319
320 protected:
321 T task_source_;
322
323 DISALLOW_COPY_AND_ASSIGN(DecoratorWithTaskSource);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53324};
325
Etienne Pierre-doraya57964d2019-07-08 22:04:55326// A RunIntent with an additional RegisteredTaskSource member.
327using RunIntentWithRegisteredTaskSource =
328 DecoratorWithTaskSource<TaskSource::RunIntent, RegisteredTaskSource>;
329
330template <class T>
331struct BASE_EXPORT BasicTransactionWithTaskSource
332 : public DecoratorWithTaskSource<TaskSource::Transaction, T> {
333 using DecoratorWithTaskSource<TaskSource::Transaction,
334 T>::DecoratorWithTaskSource;
335
336 static BasicTransactionWithTaskSource FromTaskSource(T task_source) {
337 auto transaction = task_source->BeginTransaction();
338 return BasicTransactionWithTaskSource(std::move(task_source),
339 std::move(transaction));
340 }
341};
342
343// A Transaction with an additional scoped_refptr<TaskSource> member. Useful to
344// carry ownership of a TaskSource with an associated Transaction.
345using TransactionWithOwnedTaskSource =
346 BasicTransactionWithTaskSource<scoped_refptr<TaskSource>>;
347
348// A Transaction with an additional RegisteredTaskSource member. Useful to carry
349// a RegisteredTaskSource with an associated Transaction.
350using TransactionWithRegisteredTaskSource =
351 BasicTransactionWithTaskSource<RegisteredTaskSource>;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53352
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38353} // namespace internal
354} // namespace base
355
Gabriel Charette52fa3ae2019-04-15 21:44:37356#endif // BASE_TASK_THREAD_POOL_TASK_SOURCE_H_