xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/task_queue.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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