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