1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
base / task / sequence_manager / sequence_manager.h [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_
#define BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/task_time_observer.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/default_tick_clock.h"
namespace base {
class MessagePump;
class TaskObserver;
namespace sequence_manager {
class TimeDomain;
// SequenceManager manages TaskQueues which have different properties
// (e.g. priority, common task type) multiplexing all posted tasks into
// a single backing sequence (currently bound to a single thread, which is
// refererred as *main thread* in the comments below). SequenceManager
// implementation can be used in a various ways to apply scheduling logic.
class BASE_EXPORT SequenceManager {
public:
class Observer {
public:
virtual ~Observer() = default;
// Called back on the main thread.
virtual void OnBeginNestedRunLoop() = 0;
virtual void OnExitNestedRunLoop() = 0;
};
class BASE_EXPORT PrioritySettings {
public:
// This limit is based on an implementation detail of `TaskQueueSelector`'s
// `ActivePriorityTracker`, which can be refactored if more priorities are
// needed.
static constexpr size_t kMaxPriorities = sizeof(size_t) * 8 - 1;
static PrioritySettings CreateDefault();
template <typename T,
typename = typename std::enable_if_t<std::is_enum_v<T>>>
PrioritySettings(T priority_count, T default_priority)
: PrioritySettings(
static_cast<TaskQueue::QueuePriority>(priority_count),
static_cast<TaskQueue::QueuePriority>(default_priority)) {
static_assert(
std::is_same_v<std::underlying_type_t<T>, TaskQueue::QueuePriority>,
"Enumerated priorites must have the same underlying type as "
"TaskQueue::QueuePriority");
}
PrioritySettings(TaskQueue::QueuePriority priority_count,
TaskQueue::QueuePriority default_priority);
~PrioritySettings();
PrioritySettings(PrioritySettings&&) noexcept;
PrioritySettings& operator=(PrioritySettings&&);
TaskQueue::QueuePriority priority_count() const { return priority_count_; }
TaskQueue::QueuePriority default_priority() const {
return default_priority_;
}
#if BUILDFLAG(ENABLE_BASE_TRACING)
void SetProtoPriorityConverter(
perfetto::protos::pbzero::SequenceManagerTask::Priority (
*proto_priority_converter)(TaskQueue::QueuePriority)) {
proto_priority_converter_ = proto_priority_converter;
}
perfetto::protos::pbzero::SequenceManagerTask::Priority TaskPriorityToProto(
TaskQueue::QueuePriority priority) const;
#endif
private:
TaskQueue::QueuePriority priority_count_;
TaskQueue::QueuePriority default_priority_;
#if BUILDFLAG(ENABLE_BASE_TRACING)
perfetto::protos::pbzero::SequenceManagerTask::Priority (
*proto_priority_converter_)(TaskQueue::QueuePriority) = nullptr;
#endif
#if DCHECK_IS_ON()
public:
PrioritySettings(
TaskQueue::QueuePriority priority_count,
TaskQueue::QueuePriority default_priority,
std::vector<TimeDelta> per_priority_cross_thread_task_delay,
std::vector<TimeDelta> per_priority_same_thread_task_delay);
const std::vector<TimeDelta>& per_priority_cross_thread_task_delay() const
LIFETIME_BOUND {
return per_priority_cross_thread_task_delay_;
}
const std::vector<TimeDelta>& per_priority_same_thread_task_delay() const
LIFETIME_BOUND {
return per_priority_same_thread_task_delay_;
}
private:
// Scheduler policy induced raciness is an area of concern. This lets us
// apply an extra delay per priority for cross thread posting.
std::vector<TimeDelta> per_priority_cross_thread_task_delay_;
// Like the above but for same thread posting.
std::vector<TimeDelta> per_priority_same_thread_task_delay_;
#endif
};
// Settings defining the desired SequenceManager behaviour.
struct BASE_EXPORT Settings {
class Builder;
Settings();
Settings(const Settings&) = delete;
Settings& operator=(const Settings&) = delete;
// In the future MessagePump (which is move-only) will also be a setting,
// so we are making Settings move-only in preparation.
Settings(Settings&& move_from) noexcept;
~Settings();
MessagePumpType message_loop_type = MessagePumpType::DEFAULT;
raw_ptr<const TickClock, DanglingUntriaged> clock =
DefaultTickClock::GetInstance();
// Whether or not queueing timestamp will be added to tasks.
bool add_queue_time_to_tasks = false;
// Whether many tasks may run between each check for native work.
bool can_run_tasks_by_batches = false;
PrioritySettings priority_settings = PrioritySettings::CreateDefault();
#if DCHECK_IS_ON()
// TODO(alexclarke): Consider adding command line flags to control these.
enum class TaskLogging {
kNone,
kEnabled,
kEnabledWithBacktrace,
// Logs high priority tasks and the lower priority tasks they skipped
// past. Useful for debugging test failures caused by scheduler policy
// changes.
kReorderedOnly,
};
TaskLogging task_execution_logging = TaskLogging::kNone;
// If true PostTask will emit a debug log.
bool log_post_task = false;
// If true debug logs will be emitted when a delayed task becomes eligible
// to run.
bool log_task_delay_expiry = false;
// If not zero this seeds a PRNG used by the task selection logic to choose
// a random TaskQueue for a given priority rather than the TaskQueue with
// the oldest EnqueueOrder.
uint64_t random_task_selection_seed = 0;
#endif // DCHECK_IS_ON()
};
virtual ~SequenceManager() = default;
// Binds the SequenceManager and its TaskQueues to the current thread. Should
// only be called once. Note that CreateSequenceManagerOnCurrentThread()
// performs this initialization automatically.
virtual void BindToCurrentThread() = 0;
// Returns the task runner the current task was posted on. Returns null if no
// task is currently running. Must be called on the bound thread.
virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;
// Finishes the initialization for a SequenceManager created via
// CreateUnboundSequenceManager(). Must not be called in any other
// circumstances. The ownership of the pump is transferred to SequenceManager.
virtual void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) = 0;
// Must be called on the main thread.
// Can be called only once, before creating TaskQueues.
// Observer must outlive the SequenceManager.
virtual void SetObserver(Observer* observer) = 0;
// Must be called on the main thread.
virtual void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
virtual void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
// Sets `time_domain` to be used by this scheduler and associated task queues.
// Only one time domain can be set at a time. `time_domain` must outlive this
// SequenceManager, even if ResetTimeDomain() is called. This has no effect on
// previously scheduled tasks and it is recommended that `time_domain` be set
// before posting any task to avoid inconsistencies in time. Otherwise,
// replacing `time_domain` is very subtle and should be reserved for developer
// only use cases (e.g. virtual time in devtools) where any flakiness caused
// by a racy time update isn't surprising.
virtual void SetTimeDomain(TimeDomain* time_domain) = 0;
// Disassociates the current `time_domain` and reverts to using
// RealTimeDomain.
virtual void ResetTimeDomain() = 0;
virtual const TickClock* GetTickClock() const = 0;
virtual TimeTicks NowTicks() const = 0;
// Returns a wake-up for the next delayed task which is not ripe for
// execution. If there are no such tasks (immediate tasks don't count),
// returns nullopt.
virtual std::optional<WakeUp> GetNextDelayedWakeUp() const = 0;
// Sets the SingleThreadTaskRunner that will be returned by
// SingleThreadTaskRunner::GetCurrentDefault on the main thread.
virtual void SetDefaultTaskRunner(
scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;
// Removes all canceled delayed tasks, and considers resizing to fit all
// internal queues.
virtual void ReclaimMemory() = 0;
// Returns true if no tasks were executed in TaskQueues that monitor
// quiescence since the last call to this method.
virtual bool GetAndClearSystemIsQuiescentBit() = 0;
// Set the number of tasks executed in a single SequenceManager invocation.
// Increasing this number reduces the overhead of the tasks dispatching
// logic at the cost of a potentially worse latency. 1 by default.
virtual void SetWorkBatchSize(int work_batch_size) = 0;
// Enables crash keys that can be set in the scope of a task which help
// to identify the culprit if upcoming work results in a crash.
// Key names must be thread-specific to avoid races and corrupted crash dumps.
virtual void EnableCrashKeys(const char* async_stack_crash_key) = 0;
virtual TaskQueue::QueuePriority GetPriorityCount() const = 0;
// Creates a `TaskQueue` and returns a `TaskQueue::Handle`for it. The queue is
// owned by the handle and shut down when the handle is destroyed. Must be
// called on the main thread.
virtual TaskQueue::Handle CreateTaskQueue(const TaskQueue::Spec& spec) = 0;
// Returns true iff this SequenceManager has no immediate work to do. I.e.
// there are no pending non-delayed tasks or delayed tasks that are due to
// run. This method ignores any pending delayed tasks that might have become
// eligible to run since the last task was executed. This is important because
// if it did tests would become flaky depending on the exact timing of this
// call. This is moderately expensive.
virtual bool IsIdleForTesting() = 0;
// The total number of posted tasks that haven't executed yet.
virtual size_t GetPendingTaskCountForTesting() const = 0;
// Returns a JSON string which describes all pending tasks.
virtual std::string DescribeAllPendingTasks() const = 0;
// While Now() is less than `prioritize_until` we will alternate between a
// SequenceManager task and a yielding to the underlying sequence (e.g., the
// message pump).
virtual void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) = 0;
// Adds an observer which reports task execution. Can only be called on the
// same thread that `this` is running on.
virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
// Removes an observer which reports task execution. Can only be called on the
// same thread that `this` is running on.
virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
};
class BASE_EXPORT SequenceManager::Settings::Builder {
public:
Builder();
~Builder();
// Sets the MessagePumpType which is used to create a MessagePump.
Builder& SetMessagePumpType(MessagePumpType message_loop_type);
// Sets the TickClock the SequenceManager uses to obtain Now.
Builder& SetTickClock(const TickClock* clock);
// Whether or not queueing timestamp will be added to tasks.
Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks);
// Whether many tasks may run between each check for native work.
Builder& SetCanRunTasksByBatches(bool can_run_tasks_by_batches);
Builder& SetPrioritySettings(PrioritySettings settings);
#if DCHECK_IS_ON()
// Controls task execution logging.
Builder& SetTaskLogging(TaskLogging task_execution_logging);
// Whether or not PostTask will emit a debug log.
Builder& SetLogPostTask(bool log_post_task);
// Whether or not debug logs will be emitted when a delayed task becomes
// eligible to run.
Builder& SetLogTaskDelayExpiry(bool log_task_delay_expiry);
// If not zero this seeds a PRNG used by the task selection logic to choose a
// random TaskQueue for a given priority rather than the TaskQueue with the
// oldest EnqueueOrder.
Builder& SetRandomTaskSelectionSeed(uint64_t random_task_selection_seed);
#endif // DCHECK_IS_ON()
Settings Build();
private:
Settings settings_;
};
// Create SequenceManager using MessageLoop on the current thread.
// Implementation is located in sequence_manager_impl.cc.
// TODO(scheduler-dev): Remove after every thread has a SequenceManager.
BASE_EXPORT std::unique_ptr<SequenceManager>
CreateSequenceManagerOnCurrentThread(SequenceManager::Settings settings);
// Create a SequenceManager using the given MessagePump on the current thread.
// MessagePump instances can be created with
// MessagePump::CreateMessagePumpForType().
BASE_EXPORT std::unique_ptr<SequenceManager>
CreateSequenceManagerOnCurrentThreadWithPump(
std::unique_ptr<MessagePump> message_pump,
SequenceManager::Settings settings = SequenceManager::Settings());
// Create an unbound SequenceManager (typically for a future thread or because
// additional setup is required before binding). The SequenceManager can be
// initialized on the current thread and then needs to be bound and initialized
// on the target thread by calling one of the Bind*() methods.
BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
SequenceManager::Settings settings = SequenceManager::Settings());
} // namespace sequence_manager
} // namespace base
#endif // BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_