1 // Copyright 2019 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/thread_pool/task_source.h"
6
7 #include <utility>
8
9 #include "base/check_op.h"
10 #include "base/feature_list.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/task/task_features.h"
13 #include "base/task/thread_pool/task_tracker.h"
14
15 namespace base {
16 namespace internal {
17
Transaction(TaskSource * task_source)18 TaskSource::Transaction::Transaction(TaskSource* task_source)
19 : task_source_(task_source) {
20 task_source->lock_.Acquire();
21 }
22
Transaction(TaskSource::Transaction && other)23 TaskSource::Transaction::Transaction(TaskSource::Transaction&& other)
24 : task_source_(other.task_source()) {
25 other.task_source_ = nullptr;
26 }
27
~Transaction()28 TaskSource::Transaction::~Transaction() {
29 if (task_source_) {
30 Release();
31 }
32 }
33
UpdatePriority(TaskPriority priority)34 void TaskSource::Transaction::UpdatePriority(TaskPriority priority) {
35 task_source_->traits_.UpdatePriority(priority);
36 task_source_->priority_racy_.store(task_source_->traits_.priority(),
37 std::memory_order_relaxed);
38 }
39
Release()40 void TaskSource::Transaction::Release() NO_THREAD_SAFETY_ANALYSIS {
41 DCHECK(task_source_);
42 task_source_->lock_.AssertAcquired();
43 task_source_->lock_.Release();
44 task_source_ = nullptr;
45 }
46
SetImmediateHeapHandle(const HeapHandle & handle)47 void TaskSource::SetImmediateHeapHandle(const HeapHandle& handle) {
48 immediate_pq_heap_handle_ = handle;
49 }
50
ClearImmediateHeapHandle()51 void TaskSource::ClearImmediateHeapHandle() {
52 immediate_pq_heap_handle_ = HeapHandle();
53 }
54
SetDelayedHeapHandle(const HeapHandle & handle)55 void TaskSource::SetDelayedHeapHandle(const HeapHandle& handle) {
56 delayed_pq_heap_handle_ = handle;
57 }
58
ClearDelayedHeapHandle()59 void TaskSource::ClearDelayedHeapHandle() {
60 delayed_pq_heap_handle_ = HeapHandle();
61 }
62
TaskSource(const TaskTraits & traits,TaskRunner * task_runner,TaskSourceExecutionMode execution_mode)63 TaskSource::TaskSource(const TaskTraits& traits,
64 TaskRunner* task_runner,
65 TaskSourceExecutionMode execution_mode)
66 : traits_(traits),
67 priority_racy_(traits.priority()),
68 task_runner_(task_runner),
69 execution_mode_(execution_mode) {
70 DCHECK(task_runner_ ||
71 execution_mode_ == TaskSourceExecutionMode::kParallel ||
72 execution_mode_ == TaskSourceExecutionMode::kJob);
73 }
74
~TaskSource()75 TaskSource::~TaskSource() {
76 // If this fails, a Transaction was likely held while releasing a reference to
77 // its associated task source, which lead to its destruction. Owners of
78 // Transaction must ensure to hold onto a reference of the associated task
79 // source at least until the Transaction is released to prevent UAF.
80 lock_.AssertNotHeld();
81 }
82
BeginTransaction()83 TaskSource::Transaction TaskSource::BeginTransaction() {
84 return Transaction(this);
85 }
86
ClearForTesting()87 void TaskSource::ClearForTesting() {
88 auto task = Clear(nullptr);
89 if (task) {
90 std::move(task->task).Run();
91 }
92 }
93
94 RegisteredTaskSource::RegisteredTaskSource() = default;
95
RegisteredTaskSource(std::nullptr_t)96 RegisteredTaskSource::RegisteredTaskSource(std::nullptr_t)
97 : RegisteredTaskSource() {}
98
RegisteredTaskSource(RegisteredTaskSource && other)99 RegisteredTaskSource::RegisteredTaskSource(
100 RegisteredTaskSource&& other) noexcept
101 :
102 #if DCHECK_IS_ON()
103 run_step_{std::exchange(other.run_step_, State::kInitial)},
104 #endif // DCHECK_IS_ON()
105 task_source_{std::move(other.task_source_)},
106 task_tracker_{std::exchange(other.task_tracker_, nullptr)} {
107 }
108
~RegisteredTaskSource()109 RegisteredTaskSource::~RegisteredTaskSource() {
110 Unregister();
111 }
112
113 // static
CreateForTesting(scoped_refptr<TaskSource> task_source,TaskTracker * task_tracker)114 RegisteredTaskSource RegisteredTaskSource::CreateForTesting(
115 scoped_refptr<TaskSource> task_source,
116 TaskTracker* task_tracker) {
117 return RegisteredTaskSource(std::move(task_source), task_tracker);
118 }
119
Unregister()120 scoped_refptr<TaskSource> RegisteredTaskSource::Unregister() {
121 #if DCHECK_IS_ON()
122 DCHECK_EQ(run_step_, State::kInitial);
123 #endif // DCHECK_IS_ON()
124 if (task_source_ && task_tracker_)
125 return task_tracker_->UnregisterTaskSource(std::move(task_source_));
126 return std::move(task_source_);
127 }
128
operator =(RegisteredTaskSource && other)129 RegisteredTaskSource& RegisteredTaskSource::operator=(
130 RegisteredTaskSource&& other) {
131 Unregister();
132 #if DCHECK_IS_ON()
133 run_step_ = std::exchange(other.run_step_, State::kInitial);
134 #endif // DCHECK_IS_ON()
135 task_source_ = std::move(other.task_source_);
136 task_tracker_ = std::exchange(other.task_tracker_, nullptr);
137 return *this;
138 }
139
WillRunTask()140 TaskSource::RunStatus RegisteredTaskSource::WillRunTask() {
141 TaskSource::RunStatus run_status = task_source_->WillRunTask();
142 #if DCHECK_IS_ON()
143 DCHECK_EQ(run_step_, State::kInitial);
144 if (run_status != TaskSource::RunStatus::kDisallowed)
145 run_step_ = State::kReady;
146 #endif // DCHECK_IS_ON()
147 return run_status;
148 }
149
TakeTask(TaskSource::Transaction * transaction)150 Task RegisteredTaskSource::TakeTask(TaskSource::Transaction* transaction) {
151 DCHECK(!transaction || transaction->task_source() == get());
152 #if DCHECK_IS_ON()
153 DCHECK_EQ(State::kReady, run_step_);
154 #endif // DCHECK_IS_ON()
155 return task_source_->TakeTask(transaction);
156 }
157
Clear(TaskSource::Transaction * transaction)158 absl::optional<Task> RegisteredTaskSource::Clear(
159 TaskSource::Transaction* transaction) {
160 DCHECK(!transaction || transaction->task_source() == get());
161 return task_source_->Clear(transaction);
162 }
163
DidProcessTask(TaskSource::Transaction * transaction)164 bool RegisteredTaskSource::DidProcessTask(
165 TaskSource::Transaction* transaction) {
166 DCHECK(!transaction || transaction->task_source() == get());
167 #if DCHECK_IS_ON()
168 DCHECK_EQ(State::kReady, run_step_);
169 run_step_ = State::kInitial;
170 #endif // DCHECK_IS_ON()
171 return task_source_->DidProcessTask(transaction);
172 }
173
WillReEnqueue(TimeTicks now,TaskSource::Transaction * transaction)174 bool RegisteredTaskSource::WillReEnqueue(TimeTicks now,
175 TaskSource::Transaction* transaction) {
176 DCHECK(!transaction || transaction->task_source() == get());
177 #if DCHECK_IS_ON()
178 DCHECK_EQ(State::kInitial, run_step_);
179 #endif // DCHECK_IS_ON()
180 return task_source_->WillReEnqueue(now, transaction);
181 }
182
RegisteredTaskSource(scoped_refptr<TaskSource> task_source,TaskTracker * task_tracker)183 RegisteredTaskSource::RegisteredTaskSource(
184 scoped_refptr<TaskSource> task_source,
185 TaskTracker* task_tracker)
186 : task_source_(std::move(task_source)), task_tracker_(task_tracker) {}
187
RegisteredTaskSourceAndTransaction(RegisteredTaskSource task_source_in,TaskSource::Transaction transaction_in)188 RegisteredTaskSourceAndTransaction::RegisteredTaskSourceAndTransaction(
189 RegisteredTaskSource task_source_in,
190 TaskSource::Transaction transaction_in)
191 : task_source(std::move(task_source_in)),
192 transaction(std::move(transaction_in)) {
193 DCHECK_EQ(task_source.get(), transaction.task_source());
194 }
195
196 // static:
197 RegisteredTaskSourceAndTransaction
FromTaskSource(RegisteredTaskSource task_source_in)198 RegisteredTaskSourceAndTransaction::FromTaskSource(
199 RegisteredTaskSource task_source_in) {
200 auto transaction = task_source_in->BeginTransaction();
201 return RegisteredTaskSourceAndTransaction(std::move(task_source_in),
202 std::move(transaction));
203 }
204
205 TaskSourceAndTransaction::TaskSourceAndTransaction(
206 TaskSourceAndTransaction&& other) = default;
207
208 TaskSourceAndTransaction::~TaskSourceAndTransaction() = default;
209
TaskSourceAndTransaction(scoped_refptr<TaskSource> task_source_in,TaskSource::Transaction transaction_in)210 TaskSourceAndTransaction::TaskSourceAndTransaction(
211 scoped_refptr<TaskSource> task_source_in,
212 TaskSource::Transaction transaction_in)
213 : task_source(std::move(task_source_in)),
214 transaction(std::move(transaction_in)) {
215 DCHECK_EQ(task_source.get(), transaction.task_source());
216 }
217
218 // static:
FromTaskSource(scoped_refptr<TaskSource> task_source_in)219 TaskSourceAndTransaction TaskSourceAndTransaction::FromTaskSource(
220 scoped_refptr<TaskSource> task_source_in) {
221 auto transaction = task_source_in->BeginTransaction();
222 return TaskSourceAndTransaction(std::move(task_source_in),
223 std::move(transaction));
224 }
225
226 } // namespace internal
227 } // namespace base
228