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