• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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