• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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