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_TASK_QUEUE_H_ 6 #define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_ 7 8 #include <cstdint> 9 #include <memory> 10 #include <optional> 11 #include <type_traits> 12 13 #include "base/base_export.h" 14 #include "base/check.h" 15 #include "base/task/common/checked_lock.h" 16 #include "base/task/common/lazy_now.h" 17 #include "base/task/sequence_manager/tasks.h" 18 #include "base/task/single_thread_task_runner.h" 19 #include "base/task/task_observer.h" 20 #include "base/threading/platform_thread.h" 21 #include "base/time/time.h" 22 #include "base/trace_event/base_tracing.h" 23 #include "base/trace_event/base_tracing_forward.h" 24 25 namespace perfetto { 26 class EventContext; 27 } 28 29 namespace base { 30 31 class TaskObserver; 32 33 namespace sequence_manager { 34 35 using QueueName = ::perfetto::protos::pbzero::SequenceManagerTask::QueueName; 36 37 namespace internal { 38 class SequenceManagerImpl; 39 class TaskQueueImpl; 40 } // namespace internal 41 42 // A `TaskQueue` represents an ordered list of tasks sharing common properties, 43 // e.g. priority, throttling, etc. `TaskQueue`s are associated with a 44 // `SequenceManager` instance, which chooses the next task from its set of 45 // queues. `TaskQueue`s should typically be used on a single thread since most 46 // methods are not thread safe (enforeced via CHECKs), but cross-thread task 47 // posting is supported with thread-safe task runners. 48 // 49 // A `TaskQueue` is unregistered (stops accepting and running tasks) when either 50 // its associated `TaskQueue::Handle` or `SequenceManager` is destroyed. If the 51 // handle is destroyed while the `SequenceManager` is still alive, the 52 // `SequenceManager` takes ownership of the queue and schedules it for deletion 53 // after the current task finishes. Otherwise, if the handle outlives the 54 // sequence manager, the queue is destroyed when the handle is destroyed. 55 class BASE_EXPORT TaskQueue { 56 public: 57 // Interface that lets a task queue be throttled by changing the wake up time 58 // and optionally, by inserting fences. A wake up in this context is a 59 // notification at a given time that lets this TaskQueue know of newly ripe 60 // delayed tasks if it's enabled. By delaying the desired wake up time to a 61 // different allowed wake up time, the Throttler can hold off delayed tasks 62 // that would otherwise by allowed to run sooner. 63 class BASE_EXPORT Throttler { 64 public: 65 // Invoked when the TaskQueue's next allowed wake up time is reached and is 66 // enabled, even if blocked by a fence. That wake up is defined by the last 67 // value returned from GetNextAllowedWakeUp(). 68 // This is always called on the thread this TaskQueue is associated with. 69 virtual void OnWakeUp(LazyNow* lazy_now) = 0; 70 71 // Invoked when the TaskQueue newly gets a pending immediate task and is 72 // enabled, even if blocked by a fence. Redundant calls are possible when 73 // the TaskQueue already had a pending immediate task. 74 // The implementation may use this to: 75 // - Restrict task execution by inserting/updating a fence. 76 // - Update the TaskQueue's next delayed wake up via UpdateWakeUp(). 77 // This allows the Throttler to perform additional operations later from 78 // OnWakeUp(). 79 // This is always called on the thread this TaskQueue is associated with. 80 virtual void OnHasImmediateTask() = 0; 81 82 // Invoked when the TaskQueue is enabled and wants to know when to schedule 83 // the next delayed wake-up (which happens at least every time this queue is 84 // about to cause the next wake up) provided |next_desired_wake_up|, the 85 // wake-up for the next pending delayed task in this queue (pending delayed 86 // tasks that are ripe may be ignored), or nullopt if there's no pending 87 // delayed task. |has_ready_task| indicates whether there are immediate 88 // tasks or ripe delayed tasks. The implementation should return the next 89 // allowed wake up, or nullopt if no future wake-up is necessary. 90 // This is always called on the thread this TaskQueue is associated with. 91 virtual std::optional<WakeUp> GetNextAllowedWakeUp( 92 LazyNow* lazy_now, 93 std::optional<WakeUp> next_desired_wake_up, 94 bool has_ready_task) = 0; 95 96 protected: 97 ~Throttler() = default; 98 }; 99 100 // Wrapper around a `TaskQueue`, exposed by `SequenceManager` when creating a 101 // task queue. The handle owns the underlying queue and exposes it through a 102 // unique_ptr-like interface, and it's responsible for managing the queue's 103 // lifetime, ensuring the queue is properly unregistered with the queue's 104 // `SequenceManager` when the handle is destroyed. 105 class BASE_EXPORT Handle { 106 public: 107 Handle(); 108 109 Handle(Handle&&); 110 Handle& operator=(Handle&&); 111 112 ~Handle(); 113 114 void reset(); 115 TaskQueue* get() const; 116 TaskQueue* operator->() const; 117 118 explicit operator bool() const { return !!task_queue_; } 119 120 private: 121 friend class internal::SequenceManagerImpl; 122 explicit Handle(std::unique_ptr<internal::TaskQueueImpl> task_queue); 123 124 std::unique_ptr<internal::TaskQueueImpl> task_queue_; 125 WeakPtr<internal::SequenceManagerImpl> sequence_manager_; 126 }; 127 128 // Queues with higher priority (smaller number) are selected to run before 129 // queues of lower priority. Note that there is no starvation protection, 130 // i.e., a constant stream of high priority work can mean that tasks in lower 131 // priority queues won't get to run. 132 using QueuePriority = uint8_t; 133 134 // By default there is only a single priority. Sequences making use of 135 // priorities should parameterize the `SequenceManager` with the appropriate 136 // `SequenceManager::PrioritySettings`. 137 enum class DefaultQueuePriority : QueuePriority { 138 kNormalPriority = 0, 139 140 // Must be the last entry. 141 kQueuePriorityCount = 1, 142 }; 143 144 // Options for constructing a TaskQueue. 145 struct Spec { SpecSpec146 explicit Spec(QueueName name) : name(name) {} 147 SetShouldMonitorQuiescenceSpec148 Spec SetShouldMonitorQuiescence(bool should_monitor) { 149 should_monitor_quiescence = should_monitor; 150 return *this; 151 } 152 SetShouldNotifyObserversSpec153 Spec SetShouldNotifyObservers(bool run_observers) { 154 should_notify_observers = run_observers; 155 return *this; 156 } 157 158 // Delayed fences require Now() to be sampled when posting immediate tasks 159 // which is not free. SetDelayedFencesAllowedSpec160 Spec SetDelayedFencesAllowed(bool allow_delayed_fences) { 161 delayed_fence_allowed = allow_delayed_fences; 162 return *this; 163 } 164 SetNonWakingSpec165 Spec SetNonWaking(bool non_waking_in) { 166 non_waking = non_waking_in; 167 return *this; 168 } 169 170 QueueName name; 171 bool should_monitor_quiescence = false; 172 bool should_notify_observers = true; 173 bool delayed_fence_allowed = false; 174 bool non_waking = false; 175 }; 176 177 // Information about task execution. 178 // 179 // Wall-time related methods (start_time, end_time, wall_duration) can be 180 // called only when |has_wall_time()| is true. 181 // Thread-time related mehtods (start_thread_time, end_thread_time, 182 // thread_duration) can be called only when |has_thread_time()| is true. 183 // 184 // start_* should be called after RecordTaskStart. 185 // end_* and *_duration should be called after RecordTaskEnd. 186 class BASE_EXPORT TaskTiming { 187 public: 188 enum class State { NotStarted, Running, Finished }; 189 enum class TimeRecordingPolicy { DoRecord, DoNotRecord }; 190 191 TaskTiming(bool has_wall_time, bool has_thread_time); 192 has_wall_time()193 bool has_wall_time() const { return has_wall_time_; } has_thread_time()194 bool has_thread_time() const { return has_thread_time_; } 195 start_time()196 base::TimeTicks start_time() const { 197 DCHECK(has_wall_time()); 198 return start_time_; 199 } end_time()200 base::TimeTicks end_time() const { 201 DCHECK(has_wall_time()); 202 return end_time_; 203 } wall_duration()204 base::TimeDelta wall_duration() const { 205 DCHECK(has_wall_time()); 206 return end_time_ - start_time_; 207 } start_thread_time()208 base::ThreadTicks start_thread_time() const { 209 DCHECK(has_thread_time()); 210 return start_thread_time_; 211 } end_thread_time()212 base::ThreadTicks end_thread_time() const { 213 DCHECK(has_thread_time()); 214 return end_thread_time_; 215 } thread_duration()216 base::TimeDelta thread_duration() const { 217 DCHECK(has_thread_time()); 218 return end_thread_time_ - start_thread_time_; 219 } 220 state()221 State state() const { return state_; } 222 223 void RecordTaskStart(LazyNow* now); 224 void RecordTaskEnd(LazyNow* now); 225 226 // Protected for tests. 227 protected: 228 State state_ = State::NotStarted; 229 230 bool has_wall_time_; 231 bool has_thread_time_; 232 233 base::TimeTicks start_time_; 234 base::TimeTicks end_time_; 235 base::ThreadTicks start_thread_time_; 236 base::ThreadTicks end_thread_time_; 237 }; 238 239 // An interface that lets the owner vote on whether or not the associated 240 // TaskQueue should be enabled. 241 class BASE_EXPORT QueueEnabledVoter { 242 public: 243 ~QueueEnabledVoter(); 244 245 QueueEnabledVoter(const QueueEnabledVoter&) = delete; 246 const QueueEnabledVoter& operator=(const QueueEnabledVoter&) = delete; 247 248 // Votes to enable or disable the associated TaskQueue. The TaskQueue will 249 // only be enabled if all the voters agree it should be enabled, or if there 250 // are no voters. Voters don't keep the queue alive. 251 // NOTE this must be called on the thread the associated TaskQueue was 252 // created on. 253 void SetVoteToEnable(bool enabled); 254 IsVotingToEnable()255 bool IsVotingToEnable() const { return enabled_; } 256 257 private: 258 friend class internal::TaskQueueImpl; 259 explicit QueueEnabledVoter(WeakPtr<internal::TaskQueueImpl> task_queue); 260 261 WeakPtr<internal::TaskQueueImpl> task_queue_; 262 bool enabled_ = true; 263 }; 264 265 TaskQueue(const TaskQueue&) = delete; 266 TaskQueue& operator=(const TaskQueue&) = delete; 267 virtual ~TaskQueue() = default; 268 269 // Returns an interface that allows the caller to vote on whether or not this 270 // TaskQueue is enabled. The TaskQueue will be enabled if there are no voters 271 // or if all agree it should be enabled. 272 // NOTE this must be called on the thread this TaskQueue was created by. 273 virtual std::unique_ptr<QueueEnabledVoter> CreateQueueEnabledVoter() = 0; 274 275 // NOTE this must be called on the thread this TaskQueue was created by. 276 virtual bool IsQueueEnabled() const = 0; 277 278 // Returns true if the queue is completely empty. 279 virtual bool IsEmpty() const = 0; 280 281 // Returns the number of pending tasks in the queue. 282 virtual size_t GetNumberOfPendingTasks() const = 0; 283 284 // Returns true iff this queue has immediate tasks or delayed tasks that are 285 // ripe for execution. Ignores the queue's enabled state and fences. 286 // NOTE: this must be called on the thread this TaskQueue was created by. 287 // TODO(etiennep): Rename to HasReadyTask() and add LazyNow parameter. 288 virtual bool HasTaskToRunImmediatelyOrReadyDelayedTask() const = 0; 289 290 // Returns a wake-up for the next pending delayed task (pending delayed tasks 291 // that are ripe may be ignored), ignoring Throttler is any. If there are no 292 // such tasks (immediate tasks don't count) or the queue is disabled it 293 // returns nullopt. 294 // NOTE: this must be called on the thread this TaskQueue was created by. 295 virtual std::optional<WakeUp> GetNextDesiredWakeUp() = 0; 296 297 // Can be called on any thread. 298 virtual const char* GetName() const = 0; 299 300 // Set the priority of the queue to |priority|. NOTE this must be called on 301 // the thread this TaskQueue was created by. 302 virtual void SetQueuePriority(QueuePriority priority) = 0; 303 304 // Same as above but with an enum value as the priority. 305 template <typename T, typename = typename std::enable_if_t<std::is_enum_v<T>>> SetQueuePriority(T priority)306 void SetQueuePriority(T priority) { 307 static_assert(std::is_same_v<std::underlying_type_t<T>, QueuePriority>, 308 "Enumerated priorites must have the same underlying type as " 309 "TaskQueue::QueuePriority"); 310 SetQueuePriority(static_cast<QueuePriority>(priority)); 311 } 312 313 // Returns the current queue priority. 314 virtual QueuePriority GetQueuePriority() const = 0; 315 316 // These functions can only be called on the same thread that the task queue 317 // manager executes its tasks on. 318 virtual void AddTaskObserver(TaskObserver* task_observer) = 0; 319 virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0; 320 321 enum class InsertFencePosition { 322 kNow, // Tasks posted on the queue up till this point further may run. 323 // All further tasks are blocked. 324 kBeginningOfTime, // No tasks posted on this queue may run. 325 }; 326 327 // Inserts a barrier into the task queue which prevents tasks with an enqueue 328 // order greater than the fence from running until either the fence has been 329 // removed or a subsequent fence has unblocked some tasks within the queue. 330 // Note: delayed tasks get their enqueue order set once their delay has 331 // expired, and non-delayed tasks get their enqueue order set when posted. 332 // 333 // Fences come in three flavours: 334 // - Regular (InsertFence(NOW)) - all tasks posted after this moment 335 // are blocked. 336 // - Fully blocking (InsertFence(kBeginningOfTime)) - all tasks including 337 // already posted are blocked. 338 // - Delayed (InsertFenceAt(timestamp)) - blocks all tasks posted after given 339 // point in time (must be in the future). 340 // 341 // Only one fence can be scheduled at a time. Inserting a new fence 342 // will automatically remove the previous one, regardless of fence type. 343 virtual void InsertFence(InsertFencePosition position) = 0; 344 345 // Delayed fences are only allowed for queues created with 346 // SetDelayedFencesAllowed(true) because this feature implies sampling Now() 347 // (which isn't free) for every PostTask, even those with zero delay. 348 virtual void InsertFenceAt(TimeTicks time) = 0; 349 350 // Removes any previously added fence and unblocks execution of any tasks 351 // blocked by it. 352 virtual void RemoveFence() = 0; 353 354 // Returns true if the queue has a fence but it isn't necessarily blocking 355 // execution of tasks (it may be the case if tasks enqueue order hasn't 356 // reached the number set for a fence). 357 virtual bool HasActiveFence() = 0; 358 359 // Returns true if the queue has a fence which is blocking execution of tasks. 360 virtual bool BlockedByFence() const = 0; 361 362 // Associates |throttler| to this queue. Only one throttler can be associated 363 // with this queue. |throttler| must outlive this TaskQueue, or remain valid 364 // until ResetThrottler(). 365 virtual void SetThrottler(Throttler* throttler) = 0; 366 // Disassociates the current throttler from this queue, if any. 367 virtual void ResetThrottler() = 0; 368 369 // Updates the task queue's next wake up time in its time domain, taking into 370 // account the desired run time of queued tasks and policies enforced by the 371 // throttler if any. 372 virtual void UpdateWakeUp(LazyNow* lazy_now) = 0; 373 374 // Controls whether or not the queue will emit traces events when tasks are 375 // posted to it while disabled. This only applies for the current or next 376 // period during which the queue is disabled. When the queue is re-enabled 377 // this will revert back to the default value of false. 378 virtual void SetShouldReportPostedTasksWhenDisabled(bool should_report) = 0; 379 380 // Create a task runner for this TaskQueue which will annotate all 381 // posted tasks with the given task type. 382 // Must be called on the thread this task queue is associated with. 383 // 384 // NOTE: Task runners don't keep the TaskQueue alive, so task queues can be 385 // deleted with valid task runners. Posting a task in that case will fail. 386 virtual scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner( 387 TaskType task_type) const = 0; 388 389 // Default task runner which doesn't annotate tasks with a task type. 390 virtual const scoped_refptr<SingleThreadTaskRunner>& task_runner() const = 0; 391 392 using OnTaskStartedHandler = 393 RepeatingCallback<void(const Task&, const TaskQueue::TaskTiming&)>; 394 using OnTaskCompletedHandler = 395 RepeatingCallback<void(const Task&, TaskQueue::TaskTiming*, LazyNow*)>; 396 using OnTaskPostedHandler = RepeatingCallback<void(const Task&)>; 397 using TaskExecutionTraceLogger = 398 RepeatingCallback<void(perfetto::EventContext&, const Task&)>; 399 400 // Sets a handler to subscribe for notifications about started and completed 401 // tasks. 402 virtual void SetOnTaskStartedHandler(OnTaskStartedHandler handler) = 0; 403 404 // |task_timing| may be passed in Running state and may not have the end time, 405 // so that the handler can run an additional task that is counted as a part of 406 // the main task. 407 // The handler can call TaskTiming::RecordTaskEnd, which is optional, to 408 // finalize the task, and use the resulting timing. 409 virtual void SetOnTaskCompletedHandler(OnTaskCompletedHandler handler) = 0; 410 411 // RAII handle associated with an OnTaskPostedHandler. Unregisters the handler 412 // upon destruction. 413 class OnTaskPostedCallbackHandle { 414 public: 415 OnTaskPostedCallbackHandle(const OnTaskPostedCallbackHandle&) = delete; 416 OnTaskPostedCallbackHandle& operator=(const OnTaskPostedCallbackHandle&) = 417 delete; 418 virtual ~OnTaskPostedCallbackHandle() = default; 419 420 protected: 421 OnTaskPostedCallbackHandle() = default; 422 }; 423 424 // Add a callback for adding custom functionality for processing posted task. 425 // Callback will be dispatched while holding a scheduler lock. As a result, 426 // callback should not call scheduler APIs directly, as this can lead to 427 // deadlocks. For example, PostTask should not be called directly and 428 // ScopedDeferTaskPosting::PostOrDefer should be used instead. `handler` must 429 // not be a null callback. Must be called on the thread this task queue is 430 // associated with, and the handle returned must be destroyed on the same 431 // thread. 432 [[nodiscard]] virtual std::unique_ptr<OnTaskPostedCallbackHandle> 433 AddOnTaskPostedHandler(OnTaskPostedHandler handler) = 0; 434 435 // Set a callback to fill trace event arguments associated with the task 436 // execution. 437 virtual void SetTaskExecutionTraceLogger(TaskExecutionTraceLogger logger) = 0; 438 439 protected: 440 TaskQueue() = default; 441 }; 442 443 } // namespace sequence_manager 444 } // namespace base 445 446 #endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_ 447