1 // Copyright 2017 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 #include "base/task/sequence_manager/task_queue.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/task/sequence_manager/associated_thread_id.h"
12 #include "base/task/sequence_manager/sequence_manager_impl.h"
13 #include "base/task/sequence_manager/task_queue_impl.h"
14 #include "base/threading/thread_checker.h"
15 #include "base/threading/thread_checker_impl.h"
16 #include "base/time/time.h"
17 
18 namespace base {
19 namespace sequence_manager {
20 
21 namespace {
22 
23 class NullTaskRunner final : public SingleThreadTaskRunner {
24  public:
NullTaskRunner()25   NullTaskRunner() {}
26 
PostDelayedTask(const Location & location,OnceClosure callback,TimeDelta delay)27   bool PostDelayedTask(const Location& location,
28                        OnceClosure callback,
29                        TimeDelta delay) override {
30     return false;
31   }
32 
PostNonNestableDelayedTask(const Location & location,OnceClosure callback,TimeDelta delay)33   bool PostNonNestableDelayedTask(const Location& location,
34                                   OnceClosure callback,
35                                   TimeDelta delay) override {
36     return false;
37   }
38 
RunsTasksInCurrentSequence() const39   bool RunsTasksInCurrentSequence() const override {
40     return thread_checker_.CalledOnValidThread();
41   }
42 
43  private:
44   // Ref-counted
45   ~NullTaskRunner() override = default;
46 
47   ThreadCheckerImpl thread_checker_;
48 };
49 
50 // TODO(kraynov): Move NullTaskRunner from //base/test to //base.
CreateNullTaskRunner()51 scoped_refptr<SingleThreadTaskRunner> CreateNullTaskRunner() {
52   return MakeRefCounted<NullTaskRunner>();
53 }
54 
55 }  // namespace
56 
QueueEnabledVoter(scoped_refptr<TaskQueue> task_queue)57 TaskQueue::QueueEnabledVoter::QueueEnabledVoter(
58     scoped_refptr<TaskQueue> task_queue)
59     : task_queue_(std::move(task_queue)), enabled_(true) {
60   task_queue_->AddQueueEnabledVoter(enabled_);
61 }
62 
~QueueEnabledVoter()63 TaskQueue::QueueEnabledVoter::~QueueEnabledVoter() {
64   task_queue_->RemoveQueueEnabledVoter(enabled_);
65 }
66 
SetVoteToEnable(bool enabled)67 void TaskQueue::QueueEnabledVoter::SetVoteToEnable(bool enabled) {
68   if (enabled == enabled_)
69     return;
70   enabled_ = enabled;
71   task_queue_->OnQueueEnabledVoteChanged(enabled_);
72 }
73 
AddQueueEnabledVoter(bool voter_is_enabled)74 void TaskQueue::AddQueueEnabledVoter(bool voter_is_enabled) {
75   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
76   ++voter_count_;
77   if (voter_is_enabled)
78     ++enabled_voter_count_;
79 }
80 
RemoveQueueEnabledVoter(bool voter_is_enabled)81 void TaskQueue::RemoveQueueEnabledVoter(bool voter_is_enabled) {
82   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
83   if (!impl_)
84     return;
85 
86   bool was_enabled = AreAllQueueEnabledVotersEnabled();
87   if (voter_is_enabled) {
88     --enabled_voter_count_;
89     DCHECK_GE(enabled_voter_count_, 0);
90   }
91 
92   --voter_count_;
93   DCHECK_GE(voter_count_, 0);
94 
95   bool is_enabled = AreAllQueueEnabledVotersEnabled();
96   if (was_enabled != is_enabled)
97     impl_->SetQueueEnabled(is_enabled);
98 }
99 
AreAllQueueEnabledVotersEnabled() const100 bool TaskQueue::AreAllQueueEnabledVotersEnabled() const {
101   return enabled_voter_count_ == voter_count_;
102 }
103 
OnQueueEnabledVoteChanged(bool enabled)104 void TaskQueue::OnQueueEnabledVoteChanged(bool enabled) {
105   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
106   bool was_enabled = AreAllQueueEnabledVotersEnabled();
107   if (enabled) {
108     ++enabled_voter_count_;
109     DCHECK_LE(enabled_voter_count_, voter_count_);
110   } else {
111     --enabled_voter_count_;
112     DCHECK_GE(enabled_voter_count_, 0);
113   }
114 
115   bool is_enabled = AreAllQueueEnabledVotersEnabled();
116   if (was_enabled != is_enabled)
117     impl_->SetQueueEnabled(is_enabled);
118 }
119 
TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,const TaskQueue::Spec & spec)120 TaskQueue::TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
121                      const TaskQueue::Spec& spec)
122     : impl_(std::move(impl)),
123       sequence_manager_(impl_ ? impl_->GetSequenceManagerWeakPtr() : nullptr),
124       associated_thread_((impl_ && impl_->sequence_manager())
125                              ? impl_->sequence_manager()->associated_thread()
126                              : MakeRefCounted<internal::AssociatedThreadId>()),
127       default_task_runner_(impl_ ? impl_->CreateTaskRunner(kTaskTypeNone)
128                                  : CreateNullTaskRunner()),
129       name_(impl_ ? impl_->GetName() : "") {}
130 
~TaskQueue()131 TaskQueue::~TaskQueue() {
132   ShutdownTaskQueueGracefully();
133 }
134 
ShutdownTaskQueueGracefully()135 void TaskQueue::ShutdownTaskQueueGracefully() {
136   // scoped_refptr guarantees us that this object isn't used.
137   if (!impl_)
138     return;
139   if (impl_->IsUnregistered())
140     return;
141 
142   // If we've not been unregistered then this must occur on the main thread.
143   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
144   impl_->SetObserver(nullptr);
145   if (auto* sequence_manager = impl_->sequence_manager())
146     sequence_manager->ShutdownTaskQueueGracefully(TakeTaskQueueImpl());
147 }
148 
TaskTiming(bool has_wall_time,bool has_thread_time)149 TaskQueue::TaskTiming::TaskTiming(bool has_wall_time, bool has_thread_time)
150     : has_wall_time_(has_wall_time), has_thread_time_(has_thread_time) {}
151 
RecordTaskStart(LazyNow * now)152 void TaskQueue::TaskTiming::RecordTaskStart(LazyNow* now) {
153   DCHECK_EQ(State::NotStarted, state_);
154   state_ = State::Running;
155 
156   if (has_wall_time())
157     start_time_ = now->Now();
158   if (has_thread_time())
159     start_thread_time_ = base::ThreadTicks::Now();
160 }
161 
RecordTaskEnd(LazyNow * now)162 void TaskQueue::TaskTiming::RecordTaskEnd(LazyNow* now) {
163   DCHECK(state_ == State::Running || state_ == State::Finished);
164   if (state_ == State::Finished)
165     return;
166   state_ = State::Finished;
167 
168   if (has_wall_time())
169     end_time_ = now->Now();
170   if (has_thread_time())
171     end_thread_time_ = base::ThreadTicks::Now();
172 }
173 
ShutdownTaskQueue()174 void TaskQueue::ShutdownTaskQueue() {
175   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
176   if (!impl_)
177     return;
178   if (!sequence_manager_) {
179     TakeTaskQueueImpl().reset();
180     return;
181   }
182   impl_->SetBlameContext(nullptr);
183   impl_->SetOnTaskStartedHandler(
184       internal::TaskQueueImpl::OnTaskStartedHandler());
185   impl_->SetOnTaskCompletedHandler(
186       internal::TaskQueueImpl::OnTaskCompletedHandler());
187   sequence_manager_->UnregisterTaskQueueImpl(TakeTaskQueueImpl());
188 }
189 
CreateTaskRunner(TaskType task_type)190 scoped_refptr<SingleThreadTaskRunner> TaskQueue::CreateTaskRunner(
191     TaskType task_type) {
192   // We only need to lock if we're not on the main thread.
193   base::internal::CheckedAutoLockMaybe lock(IsOnMainThread() ? &impl_lock_
194                                                              : nullptr);
195   if (!impl_)
196     return CreateNullTaskRunner();
197   return impl_->CreateTaskRunner(task_type);
198 }
199 
200 std::unique_ptr<TaskQueue::QueueEnabledVoter>
CreateQueueEnabledVoter()201 TaskQueue::CreateQueueEnabledVoter() {
202   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
203   if (!impl_)
204     return nullptr;
205   return WrapUnique(new QueueEnabledVoter(this));
206 }
207 
IsQueueEnabled() const208 bool TaskQueue::IsQueueEnabled() const {
209   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
210   if (!impl_)
211     return false;
212   return impl_->IsQueueEnabled();
213 }
214 
IsEmpty() const215 bool TaskQueue::IsEmpty() const {
216   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
217   if (!impl_)
218     return true;
219   return impl_->IsEmpty();
220 }
221 
GetNumberOfPendingTasks() const222 size_t TaskQueue::GetNumberOfPendingTasks() const {
223   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
224   if (!impl_)
225     return 0;
226   return impl_->GetNumberOfPendingTasks();
227 }
228 
HasTaskToRunImmediately() const229 bool TaskQueue::HasTaskToRunImmediately() const {
230   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
231   if (!impl_)
232     return false;
233   return impl_->HasTaskToRunImmediately();
234 }
235 
GetNextScheduledWakeUp()236 Optional<TimeTicks> TaskQueue::GetNextScheduledWakeUp() {
237   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
238   if (!impl_)
239     return nullopt;
240   return impl_->GetNextScheduledWakeUp();
241 }
242 
SetQueuePriority(TaskQueue::QueuePriority priority)243 void TaskQueue::SetQueuePriority(TaskQueue::QueuePriority priority) {
244   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
245   if (!impl_)
246     return;
247   impl_->SetQueuePriority(priority);
248 }
249 
GetQueuePriority() const250 TaskQueue::QueuePriority TaskQueue::GetQueuePriority() const {
251   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
252   if (!impl_)
253     return TaskQueue::QueuePriority::kLowPriority;
254   return impl_->GetQueuePriority();
255 }
256 
AddTaskObserver(TaskObserver * task_observer)257 void TaskQueue::AddTaskObserver(TaskObserver* task_observer) {
258   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
259   if (!impl_)
260     return;
261   impl_->AddTaskObserver(task_observer);
262 }
263 
RemoveTaskObserver(TaskObserver * task_observer)264 void TaskQueue::RemoveTaskObserver(TaskObserver* task_observer) {
265   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
266   if (!impl_)
267     return;
268   impl_->RemoveTaskObserver(task_observer);
269 }
270 
SetTimeDomain(TimeDomain * time_domain)271 void TaskQueue::SetTimeDomain(TimeDomain* time_domain) {
272   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
273   if (!impl_)
274     return;
275   impl_->SetTimeDomain(time_domain);
276 }
277 
GetTimeDomain() const278 TimeDomain* TaskQueue::GetTimeDomain() const {
279   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
280   if (!impl_)
281     return nullptr;
282   return impl_->GetTimeDomain();
283 }
284 
SetBlameContext(trace_event::BlameContext * blame_context)285 void TaskQueue::SetBlameContext(trace_event::BlameContext* blame_context) {
286   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
287   if (!impl_)
288     return;
289   impl_->SetBlameContext(blame_context);
290 }
291 
InsertFence(InsertFencePosition position)292 void TaskQueue::InsertFence(InsertFencePosition position) {
293   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
294   if (!impl_)
295     return;
296   impl_->InsertFence(position);
297 }
298 
InsertFenceAt(TimeTicks time)299 void TaskQueue::InsertFenceAt(TimeTicks time) {
300   impl_->InsertFenceAt(time);
301 }
302 
RemoveFence()303 void TaskQueue::RemoveFence() {
304   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
305   if (!impl_)
306     return;
307   impl_->RemoveFence();
308 }
309 
HasActiveFence()310 bool TaskQueue::HasActiveFence() {
311   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
312   if (!impl_)
313     return false;
314   return impl_->HasActiveFence();
315 }
316 
BlockedByFence() const317 bool TaskQueue::BlockedByFence() const {
318   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
319   if (!impl_)
320     return false;
321   return impl_->BlockedByFence();
322 }
323 
GetEnqueueOrderAtWhichWeBecameUnblocked() const324 EnqueueOrder TaskQueue::GetEnqueueOrderAtWhichWeBecameUnblocked() const {
325   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
326   if (!impl_)
327     return EnqueueOrder();
328   return impl_->GetEnqueueOrderAtWhichWeBecameUnblocked();
329 }
330 
GetName() const331 const char* TaskQueue::GetName() const {
332   return name_;
333 }
334 
SetObserver(Observer * observer)335 void TaskQueue::SetObserver(Observer* observer) {
336   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
337   if (!impl_)
338     return;
339 
340   // Observer is guaranteed to outlive TaskQueue and TaskQueueImpl lifecycle is
341   // controlled by |this|.
342   impl_->SetObserver(observer);
343 }
344 
SetShouldReportPostedTasksWhenDisabled(bool should_report)345 void TaskQueue::SetShouldReportPostedTasksWhenDisabled(bool should_report) {
346   impl_->SetShouldReportPostedTasksWhenDisabled(should_report);
347 }
348 
IsOnMainThread() const349 bool TaskQueue::IsOnMainThread() const {
350   return associated_thread_->IsBoundToCurrentThread();
351 }
352 
TakeTaskQueueImpl()353 std::unique_ptr<internal::TaskQueueImpl> TaskQueue::TakeTaskQueueImpl() {
354   base::internal::CheckedAutoLock lock(impl_lock_);
355   DCHECK(impl_);
356   return std::move(impl_);
357 }
358 
359 }  // namespace sequence_manager
360 }  // namespace base
361