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