blob: 5f6bb48e8ac321d505e06b482228464f73e4f38d [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2019 The Chromium Authors
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:382// 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 Hattori777c131a2024-04-18 10:57:4913#include "base/memory/raw_ptr_exclusion.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3814#include "base/memory/ref_counted.h"
Bartek Nowierskiccca8de2024-04-11 13:53:2715#include "base/memory/stack_allocated.h"
Etienne Pierre-doray312462152019-03-19 16:10:1716#include "base/sequence_token.h"
Gabriel Charetted35648382019-04-30 21:10:5917#include "base/task/common/checked_lock.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/task.h"
Etienne Pierre-dorayeed66832020-09-02 01:47:1120#include "base/task/thread_pool/task_source_sort_key.h"
Etienne Pierre-doray312462152019-03-19 16:10:1721#include "base/threading/sequence_local_storage_map.h"
Abdias Dagbekpof0a619d2022-08-10 02:06:2022#include "base/time/time.h"
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3823
24namespace base {
25namespace internal {
26
Etienne Pierre-dorayf7f59c32019-05-24 16:50:5327class TaskTracker;
28
Etienne Pierre-doray45fd5d52019-03-28 15:19:5529enum class TaskSourceExecutionMode {
30 kParallel,
31 kSequenced,
32 kSingleThread,
Etienne Pierre-doray36afadeb2019-07-12 21:19:4133 kJob,
34 kMax = kJob,
Etienne Pierre-doray45fd5d52019-03-28 15:19:5535};
36
Etienne Pierre-doray312462152019-03-19 16:10:1737struct BASE_EXPORT ExecutionEnvironment {
Bartek Nowierskid34494f3b2024-04-11 17:12:3638 STACK_ALLOCATED();
39
40 public:
Etienne Pierre-doray173345c2024-01-09 21:14:4141 ExecutionEnvironment(SequenceToken token) : token(token) {}
42
43 ExecutionEnvironment(SequenceToken token,
44 SequenceLocalStorageMap* sequence_local_storage,
45 SingleThreadTaskRunner* single_thread_task_runner)
46 : token(token),
47 sequence_local_storage(sequence_local_storage),
48 single_thread_task_runner(single_thread_task_runner) {}
49
50 ExecutionEnvironment(SequenceToken token,
51 SequenceLocalStorageMap* sequence_local_storage,
52 SequencedTaskRunner* sequenced_task_runner)
53 : token(token),
54 sequence_local_storage(sequence_local_storage),
55 sequenced_task_runner(sequenced_task_runner) {}
56 ~ExecutionEnvironment();
57
58 const SequenceToken token;
Bartek Nowierskid34494f3b2024-04-11 17:12:3659 SequenceLocalStorageMap* const sequence_local_storage = nullptr;
60 SingleThreadTaskRunner* const single_thread_task_runner = nullptr;
61 SequencedTaskRunner* const sequenced_task_runner = nullptr;
Etienne Pierre-doray312462152019-03-19 16:10:1762};
63
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3864// A TaskSource is a virtual class that provides a series of Tasks that must be
Etienne Pierre-doray72ed28cb2022-12-01 01:20:1665// executed immediately or in the future.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3866//
Etienne Pierre-doray72ed28cb2022-12-01 01:20:1667// When a task source has delayed tasks but no immediate tasks, the scheduler
68// must call OnBecomeReady() after HasReadyTasks(now) == true, which is
69// guaranteed once now >= GetDelayedSortKey().
70//
71// A task source is registered when it's ready to be added to the immediate
72// queue. A task source is ready to be queued when either:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0573// 1- It has new tasks that can run concurrently as a result of external
Etienne Pierre-doray72ed28cb2022-12-01 01:20:1674// operations, e.g. posting a new immediate task to an empty Sequence or
75// increasing max concurrency of a JobTaskSource;
Abdias Dagbekpof0a619d2022-08-10 02:06:2076// 2- A worker finished running a task from it and both DidProcessTask() and
77// WillReEnqueue() returned true; or
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0578// 3- A worker is about to run a task from it and WillRunTask() returned
79// kAllowedNotSaturated.
Etienne Pierre-doray72ed28cb2022-12-01 01:20:1680// 4- A delayed task became ready and OnBecomeReady() returns true.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:3881//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0582// A worker may perform the following sequence of operations on a
83// RegisteredTaskSource after obtaining it from the queue:
84// 1- Check whether a task can run with WillRunTask() (and register/enqueue the
85// task source again if not saturated).
Etienne Pierre-doray388014e2019-09-13 18:40:5286// 2- (optional) Iff (1) determined that a task can run, access the next task
87// with TakeTask().
88// 3- (optional) Execute the task.
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0589// 4- Inform the task source that a task was processed with DidProcessTask(),
Abdias Dagbekpof0a619d2022-08-10 02:06:2090// and re-enqueue the task source iff requested. The task source is ready to
91// run immediately iff WillReEnqueue() returns true.
Etienne Pierre-dorayedbacbf2019-08-23 01:56:0592// When a task source is registered multiple times, many overlapping chains of
93// operations may run concurrently, as permitted by WillRunTask(). This allows
94// tasks from the same task source to run in parallel.
95// However, the following invariants are kept:
96// - The number of workers concurrently running tasks never goes over the
97// intended concurrency.
98// - If the task source has more tasks that can run concurrently, it must be
99// queued.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38100//
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05101// Note: there is a known refcounted-ownership cycle in the ThreadPool
Etienne Pierre-doray36afadeb2019-07-12 21:19:41102// architecture: TaskSource -> TaskRunner -> TaskSource -> ... This is okay so
103// long as the other owners of TaskSource (PriorityQueue and WorkerThread in
Sean Maher36f23b52023-12-05 07:57:27104// alternation and ThreadGroup::WorkerThreadDelegateImpl::GetWork()
Etienne Pierre-doray36afadeb2019-07-12 21:19:41105// temporarily) keep running it (and taking Tasks from it as a result). A
106// dangling reference cycle would only occur should they release their reference
107// to it while it's not empty. In other words, it is only correct for them to
108// release it when DidProcessTask() returns false.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38109//
110// This class is thread-safe.
111class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
112 public:
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05113 // Indicates whether WillRunTask() allows TakeTask() to be called on a
114 // RegisteredTaskSource.
115 enum class RunStatus {
116 // TakeTask() cannot be called.
117 kDisallowed,
Etienne Pierre-doray388014e2019-09-13 18:40:52118 // TakeTask() may called, and the TaskSource has not reached its maximum
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05119 // concurrency (i.e. the TaskSource still needs to be queued).
120 kAllowedNotSaturated,
Etienne Pierre-doray388014e2019-09-13 18:40:52121 // TakeTask() may called, and the TaskSource has reached its maximum
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05122 // concurrency (i.e. the TaskSource no longer needs to be queued).
123 kAllowedSaturated,
Etienne Pierre-doraya57964d2019-07-08 22:04:55124 };
125
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38126 // A Transaction can perform multiple operations atomically on a
127 // TaskSource. While a Transaction is alive, it is guaranteed that nothing
128 // else will access the TaskSource; the TaskSource's lock is held for the
Etienne Pierre-doray5c37231a2023-02-22 03:41:37129 // lifetime of the Transaction. No Transaction must be held when ~TaskSource()
130 // is called.
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38131 class BASE_EXPORT Transaction {
Bartek Nowierskiccca8de2024-04-11 13:53:27132 STACK_ALLOCATED();
133
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38134 public:
135 Transaction(Transaction&& other);
David Bienvenu85cf749b2020-10-30 15:10:59136 Transaction(const Transaction&) = delete;
137 Transaction& operator=(const Transaction&) = delete;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38138 ~Transaction();
139
Etienne Pierre-doraya57964d2019-07-08 22:04:55140 operator bool() const { return !!task_source_; }
141
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38142 // Sets TaskSource priority to |priority|.
143 void UpdatePriority(TaskPriority priority);
144
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38145 // Returns the traits of all Tasks in the TaskSource.
146 TaskTraits traits() const { return task_source_->traits_; }
147
148 TaskSource* task_source() const { return task_source_; }
149
Etienne Pierre-doray5c37231a2023-02-22 03:41:37150 void Release();
151
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38152 protected:
153 explicit Transaction(TaskSource* task_source);
154
155 private:
156 friend class TaskSource;
157
Bartek Nowierski782d7bb2024-04-11 18:32:25158 TaskSource* task_source_ = nullptr;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38159 };
160
161 // |traits| is metadata that applies to all Tasks in the TaskSource.
Peter Kasting134ef9af2024-12-28 02:30:09162 TaskSource(const TaskTraits& traits, TaskSourceExecutionMode execution_mode);
David Bienvenu85cf749b2020-10-30 15:10:59163 TaskSource(const TaskSource&) = delete;
164 TaskSource& operator=(const TaskSource&) = delete;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38165
166 // Begins a Transaction. This method cannot be called on a thread which has an
167 // active TaskSource::Transaction.
Daniel Cheng4455c9842022-01-13 23:26:37168 [[nodiscard]] Transaction BeginTransaction();
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38169
Etienne Pierre-doray312462152019-03-19 16:10:17170 virtual ExecutionEnvironment GetExecutionEnvironment() = 0;
171
Etienne Pierre-doray36afadeb2019-07-12 21:19:41172 // Thread-safe but the returned value may immediately be obsolete. As such
173 // this should only be used as a best-effort guess of how many more workers
Etienne Pierre-doray3bca4922019-08-13 20:56:56174 // are needed. This may be called on an empty task source.
Etienne Pierre-doray36afadeb2019-07-12 21:19:41175 virtual size_t GetRemainingConcurrency() const = 0;
Etienne Pierre-doraya57964d2019-07-08 22:04:55176
Etienne Pierre-doray1f51cc032020-09-15 17:40:48177 // Returns a TaskSourceSortKey representing the priority of the TaskSource.
Etienne Pierre-dorayc64a8312022-08-25 22:02:35178 virtual TaskSourceSortKey GetSortKey() const = 0;
Abdias Dagbekpod9328742022-08-10 20:31:57179 // Returns a Timeticks object representing the next delayed runtime of the
180 // TaskSource.
181 virtual TimeTicks GetDelayedSortKey() const = 0;
Etienne Pierre-doray72ed28cb2022-12-01 01:20:16182 // Returns true if there are tasks ready to be executed. Thread-safe but the
183 // returned value may immediately be obsolete.
184 virtual bool HasReadyTasks(TimeTicks now) const = 0;
185 // Returns true if the TaskSource should be moved to the immediate queue
186 // due to ready delayed tasks. Note: Returns false if the TaskSource contains
187 // ready delayed tasks, but expects to already be in the immediate queue.
188 virtual bool OnBecomeReady() = 0;
Etienne Pierre-doray1f51cc032020-09-15 17:40:48189
Abdias Dagbekpo91eec582022-08-22 20:45:52190 // Support for IntrusiveHeap in ThreadGroup::PriorityQueue.
191 void SetImmediateHeapHandle(const HeapHandle& handle);
192 void ClearImmediateHeapHandle();
193 HeapHandle GetImmediateHeapHandle() const {
194 return immediate_pq_heap_handle_;
195 }
Chris Hamilton9c9ce502019-08-22 20:53:18196
Abdias Dagbekpo91eec582022-08-22 20:45:52197 HeapHandle immediate_heap_handle() const { return immediate_pq_heap_handle_; }
198
199 // Support for IntrusiveHeap in ThreadGroup::DelayedPriorityQueue.
200 void SetDelayedHeapHandle(const HeapHandle& handle);
201 void ClearDelayedHeapHandle();
202 HeapHandle GetDelayedHeapHandle() const { return delayed_pq_heap_handle_; }
203
204 HeapHandle delayed_heap_handle() const { return delayed_pq_heap_handle_; }
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38205
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 }
Etienne Pierre-dorayac3680742019-08-29 17:43:17211 // Returns a racy priority of the TaskSource. Can be accessed without a
212 // Transaction but may return an outdated result.
213 TaskPriority priority_racy() const {
214 return priority_racy_.load(std::memory_order_relaxed);
215 }
216 // Returns the thread policy of the TaskSource. Can be accessed without a
217 // Transaction because it is never mutated.
218 ThreadPolicy thread_policy() const { return traits_.thread_policy(); }
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38219
Etienne Pierre-doray45fd5d52019-03-28 15:19:55220 TaskSourceExecutionMode execution_mode() const { return execution_mode_; }
221
Abdias Dagbekpo6148d122022-08-23 17:36:39222 void ClearForTesting();
223
Etienne Bergeron872a6a642025-03-21 19:57:13224 const TaskTraits& traits() const { return traits_; }
225
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38226 protected:
227 virtual ~TaskSource();
228
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05229 virtual RunStatus WillRunTask() = 0;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38230
Abdias Dagbekpof0a619d2022-08-10 02:06:20231 // Implementations of TakeTask(), DidProcessTask(), WillReEnqueue(), and
232 // Clear() must ensure proper synchronization iff |transaction| is nullptr.
Etienne Pierre-dorayd9901d92019-11-09 03:21:37233 virtual Task TakeTask(TaskSource::Transaction* transaction) = 0;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05234 virtual bool DidProcessTask(TaskSource::Transaction* transaction) = 0;
Abdias Dagbekpof0a619d2022-08-10 02:06:20235 virtual bool WillReEnqueue(TimeTicks now,
236 TaskSource::Transaction* transaction) = 0;
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05237
238 // This may be called for each outstanding RegisteredTaskSource that's ready.
239 // The implementation needs to support this being called multiple times;
240 // unless it guarantees never to hand-out multiple RegisteredTaskSources that
241 // are concurrently ready.
Arthur Sonzognie5fff99c2024-02-21 15:58:24242 virtual std::optional<Task> Clear(TaskSource::Transaction* transaction) = 0;
Etienne Pierre-doray45fd5d52019-03-28 15:19:55243
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38244 // Sets TaskSource priority to |priority|.
245 void UpdatePriority(TaskPriority priority);
246
247 // The TaskTraits of all Tasks in the TaskSource.
248 TaskTraits traits_;
249
Etienne Pierre-dorayac3680742019-08-29 17:43:17250 // The cached priority for atomic access.
251 std::atomic<TaskPriority> priority_racy_;
252
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38253 // Synchronizes access to all members.
Gabriel Charetted35648382019-04-30 21:10:59254 mutable CheckedLock lock_{UniversalPredecessor()};
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38255
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05256 private:
257 friend class RefCountedThreadSafe<TaskSource>;
258 friend class RegisteredTaskSource;
259
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38260 // The TaskSource's position in its current PriorityQueue. Access is protected
261 // by the PriorityQueue's lock.
Abdias Dagbekpo91eec582022-08-22 20:45:52262 HeapHandle immediate_pq_heap_handle_;
263
264 // The TaskSource's position in its current DelayedPriorityQueue. Access is
265 // protected by the DelayedPriorityQueue's lock.
266 HeapHandle delayed_pq_heap_handle_;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38267
Etienne Pierre-doray45fd5d52019-03-28 15:19:55268 TaskSourceExecutionMode execution_mode_;
Etienne Pierre-dorayb38e0fd2019-03-18 19:35:38269};
270
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05271// Wrapper around TaskSource to signify the intent to queue and run it.
272// RegisteredTaskSource can only be created with TaskTracker and may only be
273// used by a single worker at a time. However, the same task source may be
274// registered several times, spawning multiple RegisteredTaskSources. A
275// RegisteredTaskSource resets to its initial state when WillRunTask() fails
Abdias Dagbekpof0a619d2022-08-10 02:06:20276// or after DidProcessTask() and WillReEnqueue(), so it can be used again.
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53277class BASE_EXPORT RegisteredTaskSource {
278 public:
279 RegisteredTaskSource();
280 RegisteredTaskSource(std::nullptr_t);
Etienne Pierre-doraya57964d2019-07-08 22:04:55281 RegisteredTaskSource(RegisteredTaskSource&& other) noexcept;
David Bienvenu85cf749b2020-10-30 15:10:59282 RegisteredTaskSource(const RegisteredTaskSource&) = delete;
283 RegisteredTaskSource& operator=(const RegisteredTaskSource&) = delete;
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53284 ~RegisteredTaskSource();
285
286 RegisteredTaskSource& operator=(RegisteredTaskSource&& other);
287
288 operator bool() const { return task_source_ != nullptr; }
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53289 TaskSource* operator->() const { return task_source_.get(); }
290 TaskSource* get() const { return task_source_.get(); }
291
292 static RegisteredTaskSource CreateForTesting(
293 scoped_refptr<TaskSource> task_source,
294 TaskTracker* task_tracker = nullptr);
295
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05296 // Can only be called if this RegisteredTaskSource is in its initial state.
297 // Returns the underlying task source. An Optional is used in preparation for
298 // the merge between ThreadPool and TaskQueueManager (in Blink).
299 // https://crbug.com/783309
Etienne Pierre-dorayf7f59c32019-05-24 16:50:53300 scoped_refptr<TaskSource> Unregister();
301
Etienne Pierre-dorayedbacbf2019-08-23 01:56:05302 // Informs this TaskSource that the current worker would like to run a Task
303 // from it. Can only be called if in its initial state. Returns a RunStatus
304 // that indicates if the operation is allowed (TakeTask() can be called).
305 TaskSource::RunStatus WillRunTask();