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_TASK_QUEUE_H_
6 #define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_
7 
8 #include "base/macros.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/optional.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/synchronization/lock.h"
14 #include "base/task/sequence_manager/lazy_now.h"
15 #include "base/task/sequence_manager/moveable_auto_lock.h"
16 #include "base/threading/platform_thread.h"
17 #include "base/time/time.h"
18 
19 namespace base {
20 
21 namespace trace_event {
22 class BlameContext;
23 }
24 
25 namespace sequence_manager {
26 
27 namespace internal {
28 class GracefulQueueShutdownHelper;
29 class SequenceManagerImpl;
30 class TaskQueueImpl;
31 }  // namespace internal
32 
33 class TimeDomain;
34 
35 class BASE_EXPORT TaskQueue : public SingleThreadTaskRunner {
36  public:
37   class Observer {
38    public:
39     virtual ~Observer() = default;
40 
41     // Notify observer that the time at which this queue wants to run
42     // the next task has changed. |next_wakeup| can be in the past
43     // (e.g. TimeTicks() can be used to notify about immediate work).
44     // Can be called on any thread
45     // All methods but SetObserver, SetTimeDomain and GetTimeDomain can be
46     // called on |queue|.
47     //
48     // TODO(altimin): Make it Optional<TimeTicks> to tell
49     // observer about cancellations.
50     virtual void OnQueueNextWakeUpChanged(TaskQueue* queue,
51                                           TimeTicks next_wake_up) = 0;
52   };
53 
54   // A wrapper around OnceClosure with additional metadata to be passed
55   // to PostTask and plumbed until PendingTask is created.
56   struct BASE_EXPORT PostedTask {
57     PostedTask(OnceClosure callback,
58                Location posted_from,
59                TimeDelta delay = TimeDelta(),
60                Nestable nestable = Nestable::kNestable,
61                int task_type = 0);
62     PostedTask(PostedTask&& move_from);
63     PostedTask(const PostedTask& copy_from) = delete;
64     ~PostedTask();
65 
66     OnceClosure callback;
67     Location posted_from;
68     TimeDelta delay;
69     Nestable nestable;
70     int task_type;
71   };
72 
73   // Prepare the task queue to get released.
74   // All tasks posted after this call will be discarded.
75   virtual void ShutdownTaskQueue();
76 
77   // TODO(scheduler-dev): Could we define a more clear list of priorities?
78   // See https://crbug.com/847858.
79   enum QueuePriority {
80     // Queues with control priority will run before any other queue, and will
81     // explicitly starve other queues. Typically this should only be used for
82     // private queues which perform control operations.
83     kControlPriority,
84 
85     // The selector will prioritize highest over high, normal and low; and
86     // high over normal and low; and normal over low. However it will ensure
87     // neither of the lower priority queues can be completely starved by higher
88     // priority tasks. All three of these queues will always take priority over
89     // and can starve the best effort queue.
90     kHighestPriority,
91 
92     kHighPriority,
93 
94     // Queues with normal priority are the default.
95     kNormalPriority,
96     kLowPriority,
97 
98     // Queues with best effort priority will only be run if all other queues are
99     // empty. They can be starved by the other queues.
100     kBestEffortPriority,
101     // Must be the last entry.
102     kQueuePriorityCount,
103     kFirstQueuePriority = kControlPriority,
104   };
105 
106   // Can be called on any thread.
107   static const char* PriorityToString(QueuePriority priority);
108 
109   // Options for constructing a TaskQueue.
110   struct Spec {
SpecSpec111     explicit Spec(const char* name)
112         : name(name),
113           should_monitor_quiescence(false),
114           time_domain(nullptr),
115           should_notify_observers(true) {}
116 
SetShouldMonitorQuiescenceSpec117     Spec SetShouldMonitorQuiescence(bool should_monitor) {
118       should_monitor_quiescence = should_monitor;
119       return *this;
120     }
121 
SetShouldNotifyObserversSpec122     Spec SetShouldNotifyObservers(bool run_observers) {
123       should_notify_observers = run_observers;
124       return *this;
125     }
126 
SetTimeDomainSpec127     Spec SetTimeDomain(TimeDomain* domain) {
128       time_domain = domain;
129       return *this;
130     }
131 
132     const char* name;
133     bool should_monitor_quiescence;
134     TimeDomain* time_domain;
135     bool should_notify_observers;
136   };
137 
138   // Interface to pass per-task metadata to RendererScheduler.
139   class BASE_EXPORT Task : public PendingTask {
140    public:
141     Task(PostedTask posted_task, TimeTicks desired_run_time);
142 
task_type()143     int task_type() const { return task_type_; }
144 
145    private:
146     int task_type_;
147   };
148 
149   // Information about task execution.
150   //
151   // Wall-time related methods (start_time, end_time, wall_duration) can be
152   // called only when |has_wall_time()| is true.
153   // Thread-time related mehtods (start_thread_time, end_thread_time,
154   // thread_duration) can be called only when |has_thread_time()| is true.
155   //
156   // start_* should be called after RecordTaskStart.
157   // end_* and *_duration should be called after RecordTaskEnd.
158   class BASE_EXPORT TaskTiming {
159    public:
160     TaskTiming(bool has_wall_time, bool has_thread_time);
161 
has_wall_time()162     bool has_wall_time() const { return has_wall_time_; }
has_thread_time()163     bool has_thread_time() const { return has_thread_time_; }
164 
start_time()165     base::TimeTicks start_time() const {
166       DCHECK(has_wall_time());
167       return start_time_;
168     }
end_time()169     base::TimeTicks end_time() const {
170       DCHECK(has_wall_time());
171       return end_time_;
172     }
wall_duration()173     base::TimeDelta wall_duration() const {
174       DCHECK(has_wall_time());
175       return end_time_ - start_time_;
176     }
start_thread_time()177     base::ThreadTicks start_thread_time() const {
178       DCHECK(has_thread_time());
179       return start_thread_time_;
180     }
end_thread_time()181     base::ThreadTicks end_thread_time() const {
182       DCHECK(has_thread_time());
183       return end_thread_time_;
184     }
thread_duration()185     base::TimeDelta thread_duration() const {
186       DCHECK(has_thread_time());
187       return end_thread_time_ - start_thread_time_;
188     }
189 
190     void RecordTaskStart(LazyNow* now);
191     void RecordTaskEnd(LazyNow* now);
192 
193     // Protected for tests.
194    protected:
195     bool has_wall_time_;
196     bool has_thread_time_;
197 
198     base::TimeTicks start_time_;
199     base::TimeTicks end_time_;
200     base::ThreadTicks start_thread_time_;
201     base::ThreadTicks end_thread_time_;
202   };
203 
204   // An interface that lets the owner vote on whether or not the associated
205   // TaskQueue should be enabled.
206   class QueueEnabledVoter {
207    public:
208     QueueEnabledVoter() = default;
209     virtual ~QueueEnabledVoter() = default;
210 
211     // Votes to enable or disable the associated TaskQueue. The TaskQueue will
212     // only be enabled if all the voters agree it should be enabled, or if there
213     // are no voters.
214     // NOTE this must be called on the thread the associated TaskQueue was
215     // created on.
216     virtual void SetQueueEnabled(bool enabled) = 0;
217 
218    private:
219     DISALLOW_COPY_AND_ASSIGN(QueueEnabledVoter);
220   };
221 
222   // Returns an interface that allows the caller to vote on whether or not this
223   // TaskQueue is enabled. The TaskQueue will be enabled if there are no voters
224   // or if all agree it should be enabled.
225   // NOTE this must be called on the thread this TaskQueue was created by.
226   std::unique_ptr<QueueEnabledVoter> CreateQueueEnabledVoter();
227 
228   // NOTE this must be called on the thread this TaskQueue was created by.
229   bool IsQueueEnabled() const;
230 
231   // Returns true if the queue is completely empty.
232   bool IsEmpty() const;
233 
234   // Returns the number of pending tasks in the queue.
235   size_t GetNumberOfPendingTasks() const;
236 
237   // Returns true if the queue has work that's ready to execute now.
238   // NOTE: this must be called on the thread this TaskQueue was created by.
239   bool HasTaskToRunImmediately() const;
240 
241   // Returns requested run time of next scheduled wake-up for a delayed task
242   // which is not ready to run. If there are no such tasks (immediate tasks
243   // don't count) or the queue is disabled it returns nullopt.
244   // NOTE: this must be called on the thread this TaskQueue was created by.
245   Optional<TimeTicks> GetNextScheduledWakeUp();
246 
247   // Can be called on any thread.
248   virtual const char* GetName() const;
249 
250   // Set the priority of the queue to |priority|. NOTE this must be called on
251   // the thread this TaskQueue was created by.
252   void SetQueuePriority(QueuePriority priority);
253 
254   // Returns the current queue priority.
255   QueuePriority GetQueuePriority() const;
256 
257   // These functions can only be called on the same thread that the task queue
258   // manager executes its tasks on.
259   void AddTaskObserver(MessageLoop::TaskObserver* task_observer);
260   void RemoveTaskObserver(MessageLoop::TaskObserver* task_observer);
261 
262   // Set the blame context which is entered and left while executing tasks from
263   // this task queue. |blame_context| must be null or outlive this task queue.
264   // Must be called on the thread this TaskQueue was created by.
265   void SetBlameContext(trace_event::BlameContext* blame_context);
266 
267   // Removes the task queue from the previous TimeDomain and adds it to
268   // |domain|.  This is a moderately expensive operation.
269   void SetTimeDomain(TimeDomain* domain);
270 
271   // Returns the queue's current TimeDomain.  Can be called from any thread.
272   TimeDomain* GetTimeDomain() const;
273 
274   enum class InsertFencePosition {
275     kNow,  // Tasks posted on the queue up till this point further may run.
276            // All further tasks are blocked.
277     kBeginningOfTime,  // No tasks posted on this queue may run.
278   };
279 
280   // Inserts a barrier into the task queue which prevents tasks with an enqueue
281   // order greater than the fence from running until either the fence has been
282   // removed or a subsequent fence has unblocked some tasks within the queue.
283   // Note: delayed tasks get their enqueue order set once their delay has
284   // expired, and non-delayed tasks get their enqueue order set when posted.
285   //
286   // Fences come in three flavours:
287   // - Regular (InsertFence(NOW)) - all tasks posted after this moment
288   //   are blocked.
289   // - Fully blocking (InsertFence(kBeginningOfTime)) - all tasks including
290   //   already posted are blocked.
291   // - Delayed (InsertFenceAt(timestamp)) - blocks all tasks posted after given
292   //   point in time (must be in the future).
293   //
294   // Only one fence can be scheduled at a time. Inserting a new fence
295   // will automatically remove the previous one, regardless of fence type.
296   void InsertFence(InsertFencePosition position);
297   void InsertFenceAt(TimeTicks time);
298 
299   // Removes any previously added fence and unblocks execution of any tasks
300   // blocked by it.
301   void RemoveFence();
302 
303   // Returns true if the queue has a fence but it isn't necessarily blocking
304   // execution of tasks (it may be the case if tasks enqueue order hasn't
305   // reached the number set for a fence).
306   bool HasActiveFence();
307 
308   // Returns true if the queue has a fence which is blocking execution of tasks.
309   bool BlockedByFence() const;
310 
311   void SetObserver(Observer* observer);
312 
313   // SingleThreadTaskRunner implementation
314   bool RunsTasksInCurrentSequence() const override;
315   bool PostDelayedTask(const Location& from_here,
316                        OnceClosure task,
317                        TimeDelta delay) override;
318   bool PostNonNestableDelayedTask(const Location& from_here,
319                                   OnceClosure task,
320                                   TimeDelta delay) override;
321 
322   bool PostTaskWithMetadata(PostedTask task);
323 
324  protected:
325   TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
326             const TaskQueue::Spec& spec);
327   ~TaskQueue() override;
328 
GetTaskQueueImpl()329   internal::TaskQueueImpl* GetTaskQueueImpl() const { return impl_.get(); }
330 
331  private:
332   friend class internal::SequenceManagerImpl;
333   friend class internal::TaskQueueImpl;
334 
335   bool IsOnMainThread() const;
336 
337   Optional<MoveableAutoLock> AcquireImplReadLockIfNeeded() const;
338 
339   // TaskQueue has ownership of an underlying implementation but in certain
340   // cases (e.g. detached frames) their lifetime may diverge.
341   // This method should be used to take away the impl for graceful shutdown.
342   // TaskQueue will disregard any calls or posting tasks thereafter.
343   std::unique_ptr<internal::TaskQueueImpl> TakeTaskQueueImpl();
344 
345   // |impl_| can be written to on the main thread but can be read from
346   // any thread.
347   // |impl_lock_| must be acquired when writing to |impl_| or when accessing
348   // it from non-main thread. Reading from the main thread does not require
349   // a lock.
350   mutable Lock impl_lock_;
351   std::unique_ptr<internal::TaskQueueImpl> impl_;
352 
353   const PlatformThreadId thread_id_;
354 
355   const WeakPtr<internal::SequenceManagerImpl> sequence_manager_;
356 
357   const scoped_refptr<internal::GracefulQueueShutdownHelper>
358       graceful_queue_shutdown_helper_;
359 
360   THREAD_CHECKER(main_thread_checker_);
361 
362   DISALLOW_COPY_AND_ASSIGN(TaskQueue);
363 };
364 
365 }  // namespace sequence_manager
366 }  // namespace base
367 
368 #endif  // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_H_
369