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 std::move(task.task).Run();
90 }
91
92 RegisteredTaskSource::RegisteredTaskSource() = default;
93
RegisteredTaskSource(std::nullptr_t)94 RegisteredTaskSource::RegisteredTaskSource(std::nullptr_t)
95 : RegisteredTaskSource() {}
96
RegisteredTaskSource(RegisteredTaskSource && other)97 RegisteredTaskSource::RegisteredTaskSource(
98 RegisteredTaskSource&& other) noexcept
99 :
100 #if DCHECK_IS_ON()
101 run_step_{std::exchange(other.run_step_, State::kInitial)},
102 #endif // DCHECK_IS_ON()
103 task_source_{std::move(other.task_source_)},
104 task_tracker_{std::exchange(other.task_tracker_, nullptr)} {
105 }
106
~RegisteredTaskSource()107 RegisteredTaskSource::~RegisteredTaskSource() {
108 Unregister();
109 }
110
111 // static
CreateForTesting(scoped_refptr<TaskSource> task_source,TaskTracker * task_tracker)112 RegisteredTaskSource RegisteredTaskSource::CreateForTesting(
113 scoped_refptr<TaskSource> task_source,
114 TaskTracker* task_tracker) {
115 return RegisteredTaskSource(std::move(task_source), task_tracker);
116 }
117
Unregister()118 scoped_refptr<TaskSource> RegisteredTaskSource::Unregister() {
119 #if DCHECK_IS_ON()
120 DCHECK_EQ(run_step_, State::kInitial);
121 #endif // DCHECK_IS_ON()
122 if (task_source_ && task_tracker_)
123 return task_tracker_->UnregisterTaskSource(std::move(task_source_));
124 return std::move(task_source_);
125 }
126
operator =(RegisteredTaskSource && other)127 RegisteredTaskSource& RegisteredTaskSource::operator=(
128 RegisteredTaskSource&& other) {
129 Unregister();
130 #if DCHECK_IS_ON()
131 run_step_ = std::exchange(other.run_step_, State::kInitial);
132 #endif // DCHECK_IS_ON()
133 task_source_ = std::move(other.task_source_);
134 task_tracker_ = std::exchange(other.task_tracker_, nullptr);
135 return *this;
136 }
137
WillRunTask()138 TaskSource::RunStatus RegisteredTaskSource::WillRunTask() {
139 TaskSource::RunStatus run_status = task_source_->WillRunTask();
140 #if DCHECK_IS_ON()
141 DCHECK_EQ(run_step_, State::kInitial);
142 if (run_status != TaskSource::RunStatus::kDisallowed)
143 run_step_ = State::kReady;
144 #endif // DCHECK_IS_ON()
145 return run_status;
146 }
147
TakeTask(TaskSource::Transaction * transaction)148 Task RegisteredTaskSource::TakeTask(TaskSource::Transaction* transaction) {
149 DCHECK(!transaction || transaction->task_source() == get());
150 #if DCHECK_IS_ON()
151 DCHECK_EQ(State::kReady, run_step_);
152 #endif // DCHECK_IS_ON()
153 return task_source_->TakeTask(transaction);
154 }
155
Clear(TaskSource::Transaction * transaction)156 Task RegisteredTaskSource::Clear(TaskSource::Transaction* transaction) {
157 DCHECK(!transaction || transaction->task_source() == get());
158 return task_source_->Clear(transaction);
159 }
160
DidProcessTask(TaskSource::Transaction * transaction)161 bool RegisteredTaskSource::DidProcessTask(
162 TaskSource::Transaction* transaction) {
163 DCHECK(!transaction || transaction->task_source() == get());
164 #if DCHECK_IS_ON()
165 DCHECK_EQ(State::kReady, run_step_);
166 run_step_ = State::kInitial;
167 #endif // DCHECK_IS_ON()
168 return task_source_->DidProcessTask(transaction);
169 }
170
WillReEnqueue(TimeTicks now,TaskSource::Transaction * transaction)171 bool RegisteredTaskSource::WillReEnqueue(TimeTicks now,
172 TaskSource::Transaction* transaction) {
173 DCHECK(!transaction || transaction->task_source() == get());
174 #if DCHECK_IS_ON()
175 DCHECK_EQ(State::kInitial, run_step_);
176 #endif // DCHECK_IS_ON()
177 return task_source_->WillReEnqueue(now, transaction);
178 }
179
RegisteredTaskSource(scoped_refptr<TaskSource> task_source,TaskTracker * task_tracker)180 RegisteredTaskSource::RegisteredTaskSource(
181 scoped_refptr<TaskSource> task_source,
182 TaskTracker* task_tracker)
183 : task_source_(std::move(task_source)), task_tracker_(task_tracker) {}
184
TransactionWithRegisteredTaskSource(RegisteredTaskSource task_source_in,TaskSource::Transaction transaction_in)185 TransactionWithRegisteredTaskSource::TransactionWithRegisteredTaskSource(
186 RegisteredTaskSource task_source_in,
187 TaskSource::Transaction transaction_in)
188 : task_source(std::move(task_source_in)),
189 transaction(std::move(transaction_in)) {
190 DCHECK_EQ(task_source.get(), transaction.task_source());
191 }
192
193 // static:
194 TransactionWithRegisteredTaskSource
FromTaskSource(RegisteredTaskSource task_source_in)195 TransactionWithRegisteredTaskSource::FromTaskSource(
196 RegisteredTaskSource task_source_in) {
197 auto transaction = task_source_in->BeginTransaction();
198 return TransactionWithRegisteredTaskSource(std::move(task_source_in),
199 std::move(transaction));
200 }
201
202 TaskSourceAndTransaction::TaskSourceAndTransaction(
203 TaskSourceAndTransaction&& other) = default;
204
205 TaskSourceAndTransaction::~TaskSourceAndTransaction() = default;
206
TaskSourceAndTransaction(scoped_refptr<TaskSource> task_source_in,TaskSource::Transaction transaction_in)207 TaskSourceAndTransaction::TaskSourceAndTransaction(
208 scoped_refptr<TaskSource> task_source_in,
209 TaskSource::Transaction transaction_in)
210 : task_source(std::move(task_source_in)),
211 transaction(std::move(transaction_in)) {
212 DCHECK_EQ(task_source.get(), transaction.task_source());
213 }
214
215 // static:
FromTaskSource(scoped_refptr<TaskSource> task_source_in)216 TaskSourceAndTransaction TaskSourceAndTransaction::FromTaskSource(
217 scoped_refptr<TaskSource> task_source_in) {
218 auto transaction = task_source_in->BeginTransaction();
219 return TaskSourceAndTransaction(std::move(task_source_in),
220 std::move(transaction));
221 }
222
223 } // namespace internal
224 } // namespace base
225