blob: bb9deaa0e29a73a49adadd15245540024baa9ed7 [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-doray36afadeb2019-07-12 21:19:4174 // Indicates if a task was run or skipped as a result of shutdown.
75 enum class RunResult {
76 kDidRun,
77 kSkippedAtShutdown,
Etienne Pierre-doraya57964d2019-07-08 22:04:5578 };
79
80 // Result of WillRunTask(). A single task associated with a RunIntent may be
81 // accessed with TakeTask() and run iff this evaluates to true.
82 class BASE_EXPORT RunIntent {
83 public:
84 RunIntent() = default;
85 RunIntent(RunIntent&&) noexcept;
86 ~RunIntent();
87
88 RunIntent& operator=(RunIntent&&);
89
90 operator bool() const { return !!task_source_; }
91
92 // Returns true iff the TaskSource from which this RunIntent was obtained
93 // may not run any additional tasks beyond this RunIntent as it has reached
94 // its maximum concurrency. This indicates that the TaskSource no longer
95 // needs to be queued.
Etienne Pierre-doray36afadeb2019-07-12 21:19:4196 bool IsSaturated() const { return is_saturated_ == Saturated::kYes; }
Etienne Pierre-doraya57964d2019-07-08 22:04:5597
98 const TaskSource* task_source() const { return task_source_; }
99
Etienne Pierre-doray36afadeb2019-07-12 21:19:41100 void ReleaseForTesting() {
101 DCHECK(task_source_);
102 task_source_ = nullptr;
103 }
104
Etienne Pierre-doraya57964d2019-07-08 22:04:55105 private:
106 friend class TaskSource;
107
Etienne Pierre-doray36afadeb2019-07-12 21:19:41108 // Indicates the step of a run intent chain.
109 enum class State {
110 kInitial, // After WillRunTask().
111 kTaskAcquired, // After TakeTask().
112 kCompleted, // After DidProcessTask().
113 };
114
115 RunIntent(const TaskSource* task_source, Saturated is_saturated);
Etienne Pierre-doraya57964d2019-07-08 22:04:55116
117 void Release() {
Etienne Pierre-doray36afadeb2019-07-12 21:19:41118 DCHECK_EQ(run_step_, State::kCompleted);
Etienne Pierre-doraya57964d2019-07-08 22:04:55119 DCHECK(task_source_);
120 task_source_ = nullptr;
121 }
122
123 const TaskSource* task_source_ = nullptr;
Etienne Pierre-doray36afadeb2019-07-12 21:19:41124 State run_step_ = State::kInitial;
125 Saturated is_saturated_ = Saturated::kYes;
Etienne Pierre-doraya57964d2019-07-08 22:04:55126 };
127
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38128 // A Transaction can perform multiple operations atomically on a
129 // TaskSource. While a Transaction is alive, it is guaranteed that nothing
130 // else will access the TaskSource; the TaskSource's lock is held for the
131 // lifetime of the Transaction.
132 class BASE_EXPORT Transaction {
133 public:
134 Transaction(Transaction&& other);
135 ~Transaction();
136
Etienne Pierre-doraya57964d2019-07-08 22:04:55137 operator bool() const { return !!task_source_; }
138
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38139 // Returns the next task to run from this TaskSource. This should be called
Etienne Pierre-doray36afadeb2019-07-12 21:19:41140 // only with a valid |intent|. Cannot be called on an empty TaskSource.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38141 //
142 // Because this method cannot be called on an empty TaskSource, the returned
143 // Optional<Task> is never nullptr. An Optional is used in preparation for
Gabriel Charette52fa3ae2019-04-15 21:44:37144 // the merge between ThreadPool and TaskQueueManager (in Blink).
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38145 // https://crbug.com/783309
Etienne Pierre-doray36afadeb2019-07-12 21:19:41146 Optional<Task> TakeTask(RunIntent* intent) WARN_UNUSED_RESULT;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38147
Etienne Pierre-doray36afadeb2019-07-12 21:19:41148 // Must be called once the task was run or skipped. |run_result| indicates
Etienne Pierre-doraya57964d2019-07-08 22:04:55149 // if the task executed. Cannot be called on an empty TaskSource. Returns
150 // true if the TaskSource should be queued after this operation.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41151 bool DidProcessTask(RunIntent intent,
152 RunResult run_result = RunResult::kDidRun);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38153
154 // Returns a SequenceSortKey representing the priority of the TaskSource.
155 // Cannot be called on an empty TaskSource.
156 SequenceSortKey GetSortKey() const;
157
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38158 // Sets TaskSource priority to |priority|.
159 void UpdatePriority(TaskPriority priority);
160
161 // Deletes all tasks contained in this TaskSource.
162 void Clear();
163
164 // Returns the traits of all Tasks in the TaskSource.
165 TaskTraits traits() const { return task_source_->traits_; }
166
167 TaskSource* task_source() const { return task_source_; }
168
169 protected:
170 explicit Transaction(TaskSource* task_source);
171
172 private:
173 friend class TaskSource;
174
175 TaskSource* task_source_;
176
177 DISALLOW_COPY_AND_ASSIGN(Transaction);
178 };
179
180 // |traits| is metadata that applies to all Tasks in the TaskSource.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55181 // |task_runner| is a reference to the TaskRunner feeding this TaskSource.
182 // |task_runner| can be nullptr only for tasks with no TaskRunner, in which
183 // case |execution_mode| must be kParallel. Otherwise, |execution_mode| is the
184 // execution mode of |task_runner|.
185 TaskSource(const TaskTraits& traits,
186 TaskRunner* task_runner,
187 TaskSourceExecutionMode execution_mode);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38188
189 // Begins a Transaction. This method cannot be called on a thread which has an
190 // active TaskSource::Transaction.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53191 Transaction BeginTransaction() WARN_UNUSED_RESULT;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38192
Etienne Pierre-doray312462152019-03-19 16:10:17193 virtual ExecutionEnvironment GetExecutionEnvironment() = 0;
194
Etienne Pierre-doraya57964d2019-07-08 22:04:55195 // Informs this TaskSource that an additional Task could be run. Returns a
196 // RunIntent that evaluates to true if this operation is allowed (TakeTask()
197 // can be called), or false otherwise. This function is not thread safe and
198 // must be externally synchronized (e.g. by the lock of the PriorityQueue
199 // holding the TaskSource).
200 virtual RunIntent WillRunTask() = 0;
201
Etienne Pierre-doray36afadeb2019-07-12 21:19:41202 // Thread-safe but the returned value may immediately be obsolete. As such
203 // this should only be used as a best-effort guess of how many more workers
204 // are needed.
205 virtual size_t GetRemainingConcurrency() const = 0;
Etienne Pierre-doraya57964d2019-07-08 22:04:55206
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38207 // Support for IntrusiveHeap.
208 void SetHeapHandle(const HeapHandle& handle);
209 void ClearHeapHandle();
210 HeapHandle heap_handle() const { return heap_handle_; }
211
212 // Returns the shutdown behavior of all Tasks in the TaskSource. Can be
213 // accessed without a Transaction because it is never mutated.
214 TaskShutdownBehavior shutdown_behavior() const {
215 return traits_.shutdown_behavior();
216 }
217
Etienne Pierre-doray45fd5d52019-03-28 15:19:55218 // A reference to TaskRunner is only retained between PushTask() and when
Etienne Pierre-doraya57964d2019-07-08 22:04:55219 // DidProcessTask() returns false, guaranteeing it is safe to dereference this
Etienne Pierre-doray45fd5d52019-03-28 15:19:55220 // pointer. Otherwise, the caller should guarantee such TaskRunner still
221 // exists before dereferencing.
222 TaskRunner* task_runner() const { return task_runner_; }
223
224 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
225
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38226 protected:
227 virtual ~TaskSource();
228
229 virtual Optional<Task> TakeTask() = 0;
230
Etienne Pierre-doraya57964d2019-07-08 22:04:55231 // Informs this TaskSource that a task was processed. |was_run| indicates
Etienne Pierre-doray36afadeb2019-07-12 21:19:41232 // whether the task executed or not. Returns true if the TaskSource
Etienne Pierre-doraya57964d2019-07-08 22:04:55233 // should be queued after this operation.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41234 virtual bool DidProcessTask(RunResult run_result) = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55235
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38236 virtual SequenceSortKey GetSortKey() const = 0;
237
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38238 virtual void Clear() = 0;
239
240 // Sets TaskSource priority to |priority|.
241 void UpdatePriority(TaskPriority priority);
242
Etienne Pierre-doraya57964d2019-07-08 22:04:55243 // Constructs and returns a RunIntent, where |is_saturated| indicates that the
244 // TaskSource has reached its maximum concurrency.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41245 RunIntent MakeRunIntent(Saturated is_saturated) const;
Etienne Pierre-doraya57964d2019-07-08 22:04:55246
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38247 // The TaskTraits of all Tasks in the TaskSource.
248 TaskTraits traits_;
249
250 private:
251 friend class RefCountedThreadSafe<TaskSource>;
252
253 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59254 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38255
256 // The TaskSource's position in its current PriorityQueue. Access is protected
257 // by the PriorityQueue's lock.
258 HeapHandle heap_handle_;
259
Etienne Pierre-doray45fd5d52019-03-28 15:19:55260 // A pointer to the TaskRunner that posts to this TaskSource, if any. The
Etienne Pierre-dorayabfecf72019-05-09 20:41:48261 // derived class is responsible for calling AddRef() when a TaskSource from
262 // which no Task is executing becomes non-empty and Release() when
Etienne Pierre-doraya57964d2019-07-08 22:04:55263 // DidProcessTask() returns false.
Etienne Pierre-doray45fd5d52019-03-28 15:19:55264 TaskRunner* task_runner_;
265
266 TaskSourceExecutionMode execution_mode_;
267
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38268 DISALLOW_COPY_AND_ASSIGN(TaskSource);
269};
270
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53271// Wrapper around TaskSource to signify the intent to queue and run it. A
272// RegisteredTaskSource can only be created with TaskTracker.
273class BASE_EXPORT RegisteredTaskSource {
274 public:
275 RegisteredTaskSource();
276 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55277 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53278 ~RegisteredTaskSource();
279
280 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
281
282 operator bool() const { return task_source_ != nullptr; }
283
284 TaskSource* operator->() const { return task_source_.get(); }
285 TaskSource* get() const { return task_source_.get(); }
286
287 static RegisteredTaskSource CreateForTesting(
288 scoped_refptr<TaskSource> task_source,
289 TaskTracker* task_tracker = nullptr);
290
291 scoped_refptr<TaskSource> Unregister();
292
293 private:
294 friend class TaskTracker;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53295 RegisteredTaskSource(scoped_refptr<TaskSource> task_source,
296 TaskTracker* task_tracker);
297
298 scoped_refptr<TaskSource> task_source_;
299 TaskTracker* task_tracker_;
300
301 DISALLOW_COPY_AND_ASSIGN(RegisteredTaskSource);
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38302};
303
Etienne Pierre-doraya57964d2019-07-08 22:04:55304// Base implementation for TransactionWith[Owned/Registered]TaskSource (with
305// Transaction as the decorator) and RunIntentWithRegisteredTaskSource (with
306// RunIntent as the decorator).
307template <class Decorator, class T>
308class BASE_EXPORT DecoratorWithTaskSource : public Decorator {
309 public:
310 DecoratorWithTaskSource() = default;
311 DecoratorWithTaskSource(std::nullptr_t) : DecoratorWithTaskSource() {}
312 DecoratorWithTaskSource(T task_source_in, Decorator decorator)
313 : Decorator(std::move(decorator)),
314 task_source_(std::move(task_source_in)) {
315 DCHECK_EQ(task_source_.get(), this->task_source());
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53316 }
Etienne Pierre-doraya57964d2019-07-08 22:04:55317 DecoratorWithTaskSource(DecoratorWithTaskSource&& other) = default;
318 ~DecoratorWithTaskSource() = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53319
Etienne Pierre-doraya57964d2019-07-08 22:04:55320 DecoratorWithTaskSource& operator=(DecoratorWithTaskSource&&) = default;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53321
Etienne Pierre-doraya57964d2019-07-08 22:04:55322 T take_task_source() { return std::move(task_source_); }
323
324 protected:
325 T task_source_;
326
327 DISALLOW_COPY_AND_ASSIGN(DecoratorWithTaskSource);
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53328};
329
Etienne Pierre-doraya57964d2019-07-08 22:04:55330// A RunIntent with an additional RegisteredTaskSource member.
331using RunIntentWithRegisteredTaskSource =
332 DecoratorWithTaskSource<TaskSource::RunIntent, RegisteredTaskSource>;
333
334template <class T>
335struct BASE_EXPORT BasicTransactionWithTaskSource
336 : public DecoratorWithTaskSource<TaskSource::Transaction, T> {
337 using DecoratorWithTaskSource<TaskSource::Transaction,
338 T>::DecoratorWithTaskSource;
339
340 static BasicTransactionWithTaskSource FromTaskSource(T task_source) {
341 auto transaction = task_source->BeginTransaction();
342 return BasicTransactionWithTaskSource(std::move(task_source),
343 std::move(transaction));
344 }
345};
346
347// A Transaction with an additional scoped_refptr<TaskSource> member. Useful to
348// carry ownership of a TaskSource with an associated Transaction.
349using TransactionWithOwnedTaskSource =
350 BasicTransactionWithTaskSource<scoped_refptr<TaskSource>>;
351
352// A Transaction with an additional RegisteredTaskSource member. Useful to carry
353// a RegisteredTaskSource with an associated Transaction.
354using TransactionWithRegisteredTaskSource =
355 BasicTransactionWithTaskSource<RegisteredTaskSource>;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53356
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38357} // namespace internal
358} // namespace base
359
Gabriel Charette52fa3ae2019-04-15 21:44:37360#endif // BASE_TASK_THREAD_POOL_TASK_SOURCE_H_