1 // Copyright 2018 The Chromium Authors. All rights reserved.
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 <utility>
10 
11 #include "base/macros.h"
12 #include "base/message_loop/message_pump_type.h"
13 #include "base/message_loop/timer_slack.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/task/sequence_manager/task_queue_impl.h"
17 #include "base/task/sequence_manager/task_time_observer.h"
18 #include "base/time/default_tick_clock.h"
19 
20 namespace base {
21 
22 class MessagePump;
23 class TaskObserver;
24 
25 namespace sequence_manager {
26 
27 class TimeDomain;
28 
29 // Represent outstanding work the sequence underlying a SequenceManager (e.g.,
30 // a native system task for drawing the UI). As long as this handle is alive,
31 // the work is considered to be pending.
32 class NativeWorkHandle {
33  public:
34   virtual ~NativeWorkHandle();
35   NativeWorkHandle(const NativeWorkHandle&) = delete;
36 
37  protected:
38   NativeWorkHandle() = default;
39 };
40 
41 // SequenceManager manages TaskQueues which have different properties
42 // (e.g. priority, common task type) multiplexing all posted tasks into
43 // a single backing sequence (currently bound to a single thread, which is
44 // refererred as *main thread* in the comments below). SequenceManager
45 // implementation can be used in a various ways to apply scheduling logic.
46 class BASE_EXPORT SequenceManager {
47  public:
48   class Observer {
49    public:
50     virtual ~Observer() = default;
51     // Called back on the main thread.
52     virtual void OnBeginNestedRunLoop() = 0;
53     virtual void OnExitNestedRunLoop() = 0;
54   };
55 
56   struct MetricRecordingSettings {
57     // This parameter will be updated for consistency on creation (setting
58     // value to 0 when ThreadTicks are not supported).
59     MetricRecordingSettings(double task_sampling_rate_for_recording_cpu_time);
60 
61     // The proportion of the tasks for which the cpu time will be
62     // sampled or 0 if this is not enabled.
63     // Since randomised sampling requires the use of Rand(), it is enabled only
64     // on platforms which support it.
65     // If it is 1 then cpu time is measured for each task, so the integral
66     // metrics (as opposed to per-task metrics) can be recorded.
67     double task_sampling_rate_for_recording_cpu_time = 0;
68 
records_cpu_time_for_some_tasksMetricRecordingSettings69     bool records_cpu_time_for_some_tasks() const {
70       return task_sampling_rate_for_recording_cpu_time > 0.0;
71     }
72 
records_cpu_time_for_all_tasksMetricRecordingSettings73     bool records_cpu_time_for_all_tasks() const {
74       return task_sampling_rate_for_recording_cpu_time == 1.0;
75     }
76   };
77 
78   // Settings defining the desired SequenceManager behaviour: the type of the
79   // MessageLoop and whether randomised sampling should be enabled.
80   struct BASE_EXPORT Settings {
81     class Builder;
82 
83     Settings();
84     // In the future MessagePump (which is move-only) will also be a setting,
85     // so we are making Settings move-only in preparation.
86     Settings(Settings&& move_from) noexcept;
87 
88     MessagePumpType message_loop_type = MessagePumpType::DEFAULT;
89     bool randomised_sampling_enabled = false;
90     const TickClock* clock = DefaultTickClock::GetInstance();
91 
92     // If true, add the timestamp the task got queued to the task.
93     bool add_queue_time_to_tasks = false;
94 
95 #if DCHECK_IS_ON()
96     // TODO(alexclarke): Consider adding command line flags to control these.
97     enum class TaskLogging {
98       kNone,
99       kEnabled,
100       kEnabledWithBacktrace,
101 
102       // Logs high priority tasks and the lower priority tasks they skipped
103       // past.  Useful for debugging test failures caused by scheduler policy
104       // changes.
105       kReorderedOnly,
106     };
107     TaskLogging task_execution_logging = TaskLogging::kNone;
108 
109     // If true PostTask will emit a debug log.
110     bool log_post_task = false;
111 
112     // If true debug logs will be emitted when a delayed task becomes eligible
113     // to run.
114     bool log_task_delay_expiry = false;
115 
116     // If true usages of the RunLoop API will be logged.
117     bool log_runloop_quit_and_quit_when_idle = false;
118 
119     // Scheduler policy induced raciness is an area of concern. This lets us
120     // apply an extra delay per priority for cross thread posting.
121     std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
122         per_priority_cross_thread_task_delay;
123 
124     // Like the above but for same thread posting.
125     std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
126         per_priority_same_thread_task_delay;
127 
128     // If not zero this seeds a PRNG used by the task selection logic to choose
129     // a random TaskQueue for a given priority rather than the TaskQueue with
130     // the oldest EnqueueOrder.
131     int random_task_selection_seed = 0;
132 
133 #endif  // DCHECK_IS_ON()
134 
135     DISALLOW_COPY_AND_ASSIGN(Settings);
136   };
137 
138   virtual ~SequenceManager() = default;
139 
140   // Binds the SequenceManager and its TaskQueues to the current thread. Should
141   // only be called once. Note that CreateSequenceManagerOnCurrentThread()
142   // performs this initialization automatically.
143   virtual void BindToCurrentThread() = 0;
144 
145   // Returns the task runner the current task was posted on. Returns null if no
146   // task is currently running. Must be called on the bound thread.
147   virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;
148 
149   // Finishes the initialization for a SequenceManager created via
150   // CreateUnboundSequenceManager(). Must not be called in any other
151   // circumstances. The ownership of the pump is transferred to SequenceManager.
152   virtual void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) = 0;
153 
154   // Must be called on the main thread.
155   // Can be called only once, before creating TaskQueues.
156   // Observer must outlive the SequenceManager.
157   virtual void SetObserver(Observer* observer) = 0;
158 
159   // Must be called on the main thread.
160   virtual void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
161   virtual void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
162 
163   // Registers a TimeDomain with SequenceManager.
164   // TaskQueues must only be created with a registered TimeDomain.
165   // Conversely, any TimeDomain must remain registered until no
166   // TaskQueues (using that TimeDomain) remain.
167   virtual void RegisterTimeDomain(TimeDomain* time_domain) = 0;
168   virtual void UnregisterTimeDomain(TimeDomain* time_domain) = 0;
169 
170   virtual TimeDomain* GetRealTimeDomain() const = 0;
171   virtual const TickClock* GetTickClock() const = 0;
172   virtual TimeTicks NowTicks() const = 0;
173 
174   // Sets the SingleThreadTaskRunner that will be returned by
175   // ThreadTaskRunnerHandle::Get on the main thread.
176   virtual void SetDefaultTaskRunner(
177       scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;
178 
179   // Removes all canceled delayed tasks, and considers resizing to fit all
180   // internal queues.
181   virtual void ReclaimMemory() = 0;
182 
183   // Returns true if no tasks were executed in TaskQueues that monitor
184   // quiescence since the last call to this method.
185   virtual bool GetAndClearSystemIsQuiescentBit() = 0;
186 
187   // Set the number of tasks executed in a single SequenceManager invocation.
188   // Increasing this number reduces the overhead of the tasks dispatching
189   // logic at the cost of a potentially worse latency. 1 by default.
190   virtual void SetWorkBatchSize(int work_batch_size) = 0;
191 
192   // Requests desired timer precision from the OS.
193   // Has no effect on some platforms.
194   virtual void SetTimerSlack(TimerSlack timer_slack) = 0;
195 
196   // Enables crash keys that can be set in the scope of a task which help
197   // to identify the culprit if upcoming work results in a crash.
198   // Key names must be thread-specific to avoid races and corrupted crash dumps.
199   virtual void EnableCrashKeys(const char* async_stack_crash_key) = 0;
200 
201   // Returns the metric recording configuration for the current SequenceManager.
202   virtual const MetricRecordingSettings& GetMetricRecordingSettings() const = 0;
203 
204   // Creates a task queue with the given type, |spec| and args.
205   // Must be called on the main thread.
206   // TODO(scheduler-dev): SequenceManager should not create TaskQueues.
207   template <typename TaskQueueType, typename... Args>
CreateTaskQueueWithType(const TaskQueue::Spec & spec,Args &&...args)208   scoped_refptr<TaskQueueType> CreateTaskQueueWithType(
209       const TaskQueue::Spec& spec,
210       Args&&... args) {
211     return WrapRefCounted(new TaskQueueType(CreateTaskQueueImpl(spec), spec,
212                                             std::forward<Args>(args)...));
213   }
214 
215   // Creates a vanilla TaskQueue rather than a user type derived from it. This
216   // should be used if you don't wish to sub class TaskQueue.
217   // Must be called on the main thread.
218   virtual scoped_refptr<TaskQueue> CreateTaskQueue(
219       const TaskQueue::Spec& spec) = 0;
220 
221   // Returns true iff this SequenceManager has no immediate work to do. I.e.
222   // there are no pending non-delayed tasks or delayed tasks that are due to
223   // run. This method ignores any pending delayed tasks that might have become
224   // eligible to run since the last task was executed. This is important because
225   // if it did tests would become flaky depending on the exact timing of this
226   // call. This is moderately expensive.
227   virtual bool IsIdleForTesting() = 0;
228 
229   // The total number of posted tasks that haven't executed yet.
230   virtual size_t GetPendingTaskCountForTesting() const = 0;
231 
232   // Returns a JSON string which describes all pending tasks.
233   virtual std::string DescribeAllPendingTasks() const = 0;
234 
235   // Indicates that the underlying sequence (e.g., the message pump) has pending
236   // work at priority |priority|. If the priority of the work in this
237   // SequenceManager is lower, it will yield to let the native work run. The
238   // native work is assumed to remain pending while the returned handle is
239   // valid.
240   //
241   // Must be called on the main thread, and the returned handle must also be
242   // deleted on the main thread.
243   virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
244       TaskQueue::QueuePriority priority) = 0;
245 
246   // Adds an observer which reports task execution. Can only be called on the
247   // same thread that |this| is running on.
248   virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
249 
250   // Removes an observer which reports task execution. Can only be called on the
251   // same thread that |this| is running on.
252   virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
253 
254  protected:
255   virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
256       const TaskQueue::Spec& spec) = 0;
257 };
258 
259 class BASE_EXPORT SequenceManager::Settings::Builder {
260  public:
261   Builder();
262   ~Builder();
263 
264   // Sets the MessagePumpType which is used to create a MessagePump.
265   Builder& SetMessagePumpType(MessagePumpType message_loop_type);
266 
267   Builder& SetRandomisedSamplingEnabled(bool randomised_sampling_enabled);
268 
269   // Sets the TickClock the SequenceManager uses to obtain Now.
270   Builder& SetTickClock(const TickClock* clock);
271 
272   // Whether or not queueing timestamp will be added to tasks.
273   Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks);
274 
275 #if DCHECK_IS_ON()
276   // Controls task execution logging.
277   Builder& SetTaskLogging(TaskLogging task_execution_logging);
278 
279   // Whether or not PostTask will emit a debug log.
280   Builder& SetLogPostTask(bool log_post_task);
281 
282   // Whether or not debug logs will be emitted when a delayed task becomes
283   // eligible to run.
284   Builder& SetLogTaskDelayExpiry(bool log_task_delay_expiry);
285 
286   // Whether or not usages of the RunLoop API will be logged.
287   Builder& SetLogRunloopQuitAndQuitWhenIdle(
288       bool log_runloop_quit_and_quit_when_idle);
289 
290   // Scheduler policy induced raciness is an area of concern. This lets us
291   // apply an extra delay per priority for cross thread posting.
292   Builder& SetPerPriorityCrossThreadTaskDelay(
293       std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
294           per_priority_cross_thread_task_delay);
295 
296   // Scheduler policy induced raciness is an area of concern. This lets us
297   // apply an extra delay per priority for same thread posting.
298   Builder& SetPerPrioritySameThreadTaskDelay(
299       std::array<TimeDelta, TaskQueue::kQueuePriorityCount>
300           per_priority_same_thread_task_delay);
301 
302   // If not zero this seeds a PRNG used by the task selection logic to choose a
303   // random TaskQueue for a given priority rather than the TaskQueue with the
304   // oldest EnqueueOrder.
305   Builder& SetRandomTaskSelectionSeed(int random_task_selection_seed);
306 
307 #endif  // DCHECK_IS_ON()
308 
309   Settings Build();
310 
311  private:
312   Settings settings_;
313 };
314 
315 // Create SequenceManager using MessageLoop on the current thread.
316 // Implementation is located in sequence_manager_impl.cc.
317 // TODO(scheduler-dev): Remove after every thread has a SequenceManager.
318 BASE_EXPORT std::unique_ptr<SequenceManager>
319 CreateSequenceManagerOnCurrentThread(SequenceManager::Settings settings);
320 
321 // Create a SequenceManager using the given MessagePump on the current thread.
322 // MessagePump instances can be created with
323 // MessagePump::CreateMessagePumpForType().
324 BASE_EXPORT std::unique_ptr<SequenceManager>
325 CreateSequenceManagerOnCurrentThreadWithPump(
326     std::unique_ptr<MessagePump> message_pump,
327     SequenceManager::Settings settings = SequenceManager::Settings());
328 
329 // Create an unbound SequenceManager (typically for a future thread or because
330 // additional setup is required before binding). The SequenceManager can be
331 // initialized on the current thread and then needs to be bound and initialized
332 // on the target thread by calling one of the Bind*() methods.
333 BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
334     SequenceManager::Settings settings = SequenceManager::Settings());
335 
336 }  // namespace sequence_manager
337 }  // namespace base
338 
339 #endif  // BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_
340