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 355 356 357 358
|
// 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>
requires(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_;
}
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;
private:
TaskQueue::QueuePriority priority_count_;
TaskQueue::QueuePriority default_priority_;
perfetto::protos::pbzero::SequenceManagerTask::Priority (
*proto_priority_converter_)(TaskQueue::QueuePriority) = nullptr;
#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;
// Whether or not CPU time should be sampled for a fixed percentage of
// tasks.
bool sample_cpu_time = false;
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;
// Gets a pointer to the message pump that this sequence manager is bound to,
// if any.
virtual MessagePump* GetMessagePump() const = 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;
// 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);
// Whether or not CPU time will be sampled for tasks at a fixed sampling
// ratio.
Builder& SetShouldSampleCPUTime(bool enable);
// 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_
|