xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/sequence_manager.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_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