1 // Copyright 2013 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 #include "base/task/deferred_sequenced_task_runner.h"
6 #include "base/task/common/scoped_defer_task_posting.h"
7
8 #include <utility>
9
10 #include "base/check.h"
11 #include "base/functional/bind.h"
12
13 namespace base {
14
DeferredTask()15 DeferredSequencedTaskRunner::DeferredTask::DeferredTask()
16 : is_non_nestable(false) {
17 }
18
19 DeferredSequencedTaskRunner::DeferredTask::DeferredTask(DeferredTask&& other) =
20 default;
21
22 DeferredSequencedTaskRunner::DeferredTask::~DeferredTask() = default;
23
24 DeferredSequencedTaskRunner::DeferredTask&
25 DeferredSequencedTaskRunner::DeferredTask::operator=(DeferredTask&& other) =
26 default;
27
DeferredSequencedTaskRunner(scoped_refptr<SequencedTaskRunner> target_task_runner)28 DeferredSequencedTaskRunner::DeferredSequencedTaskRunner(
29 scoped_refptr<SequencedTaskRunner> target_task_runner)
30 : created_thread_id_(PlatformThread::CurrentId()),
31 target_task_runner_(std::move(target_task_runner)) {
32 #if DCHECK_IS_ON()
33 AutoLock lock(lock_);
34 DCHECK(target_task_runner_);
35 #endif
36 task_runner_atomic_ptr_.store(target_task_runner_.get(),
37 std::memory_order_release);
38 }
39
DeferredSequencedTaskRunner()40 DeferredSequencedTaskRunner::DeferredSequencedTaskRunner()
41 : created_thread_id_(PlatformThread::CurrentId()) {}
42
PostDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)43 bool DeferredSequencedTaskRunner::PostDelayedTask(const Location& from_here,
44 OnceClosure task,
45 TimeDelta delay) {
46 // Do not process new PostTasks while we are handling a PostTask (tracing
47 // has to do this) as it can lead to a deadlock and defer it instead.
48 ScopedDeferTaskPosting disallow_task_posting;
49
50 AutoLock lock(lock_);
51 if (started_) {
52 DCHECK(deferred_tasks_queue_.empty());
53 return target_task_runner_->PostDelayedTask(from_here, std::move(task),
54 delay);
55 }
56
57 QueueDeferredTask(from_here, std::move(task), delay,
58 false /* is_non_nestable */);
59 return true;
60 }
61
RunsTasksInCurrentSequence() const62 bool DeferredSequencedTaskRunner::RunsTasksInCurrentSequence() const {
63 // task_runner_atomic_ptr_ cannot change once it has been initialized, so it's
64 // safe to access it without lock.
65 SequencedTaskRunner* task_runner_ptr =
66 task_runner_atomic_ptr_.load(std::memory_order_acquire);
67 if (task_runner_ptr) {
68 return task_runner_ptr->RunsTasksInCurrentSequence();
69 }
70
71 return created_thread_id_ == PlatformThread::CurrentId();
72 }
73
PostNonNestableDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)74 bool DeferredSequencedTaskRunner::PostNonNestableDelayedTask(
75 const Location& from_here,
76 OnceClosure task,
77 TimeDelta delay) {
78 AutoLock lock(lock_);
79 if (started_) {
80 DCHECK(deferred_tasks_queue_.empty());
81 return target_task_runner_->PostNonNestableDelayedTask(
82 from_here, std::move(task), delay);
83 }
84 QueueDeferredTask(from_here, std::move(task), delay,
85 true /* is_non_nestable */);
86 return true;
87 }
88
Start()89 void DeferredSequencedTaskRunner::Start() {
90 AutoLock lock(lock_);
91 StartImpl();
92 }
93
StartWithTaskRunner(scoped_refptr<SequencedTaskRunner> target_task_runner)94 void DeferredSequencedTaskRunner::StartWithTaskRunner(
95 scoped_refptr<SequencedTaskRunner> target_task_runner) {
96 AutoLock lock(lock_);
97 DCHECK(!target_task_runner_);
98 DCHECK(target_task_runner);
99 target_task_runner_ = std::move(target_task_runner);
100 task_runner_atomic_ptr_.store(target_task_runner_.get(),
101 std::memory_order_release);
102 StartImpl();
103 }
104
Started() const105 bool DeferredSequencedTaskRunner::Started() const {
106 AutoLock lock(lock_);
107 return started_;
108 }
109
110 DeferredSequencedTaskRunner::~DeferredSequencedTaskRunner() = default;
111
QueueDeferredTask(const Location & from_here,OnceClosure task,TimeDelta delay,bool is_non_nestable)112 void DeferredSequencedTaskRunner::QueueDeferredTask(const Location& from_here,
113 OnceClosure task,
114 TimeDelta delay,
115 bool is_non_nestable) {
116 lock_.AssertAcquired();
117
118 // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
119 // for details.
120 CHECK(task);
121
122 DeferredTask deferred_task;
123 deferred_task.posted_from = from_here;
124 deferred_task.task = std::move(task);
125 deferred_task.delay = delay;
126 deferred_task.is_non_nestable = is_non_nestable;
127 deferred_tasks_queue_.push_back(std::move(deferred_task));
128 }
129
StartImpl()130 void DeferredSequencedTaskRunner::StartImpl() {
131 lock_.AssertAcquired(); // Callers should have grabbed the lock.
132 DCHECK(!started_);
133 started_ = true;
134 DCHECK(target_task_runner_);
135 for (auto& task : deferred_tasks_queue_) {
136 if (task.is_non_nestable) {
137 target_task_runner_->PostNonNestableDelayedTask(
138 task.posted_from, std::move(task.task), task.delay);
139 } else {
140 target_task_runner_->PostDelayedTask(task.posted_from,
141 std::move(task.task), task.delay);
142 }
143 }
144 deferred_tasks_queue_.clear();
145 }
146
147 } // namespace base
148