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