| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/task/thread_pool/task_source.h" |
| |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task/task_features.h" |
| #include "base/task/thread_pool/task_tracker.h" |
| |
| namespace base { |
| namespace internal { |
| |
| TaskSource::Transaction::Transaction(TaskSource* task_source) |
| : task_source_(task_source) { |
| task_source->lock_.Acquire(); |
| } |
| |
| TaskSource::Transaction::Transaction(TaskSource::Transaction&& other) |
| : task_source_(other.task_source()) { |
| other.task_source_ = nullptr; |
| } |
| |
| TaskSource::Transaction::~Transaction() { |
| if (task_source_) { |
| Release(); |
| } |
| } |
| |
| void TaskSource::Transaction::UpdatePriority(TaskPriority priority) { |
| task_source_->traits_.UpdatePriority(priority); |
| task_source_->priority_racy_.store(task_source_->traits_.priority(), |
| std::memory_order_relaxed); |
| } |
| |
| void TaskSource::Transaction::Release() NO_THREAD_SAFETY_ANALYSIS { |
| DCHECK(task_source_); |
| task_source_->lock_.AssertAcquired(); |
| task_source_->lock_.Release(); |
| task_source_ = nullptr; |
| } |
| |
| void TaskSource::SetImmediateHeapHandle(const HeapHandle& handle) { |
| immediate_pq_heap_handle_ = handle; |
| } |
| |
| void TaskSource::ClearImmediateHeapHandle() { |
| immediate_pq_heap_handle_ = HeapHandle(); |
| } |
| |
| void TaskSource::SetDelayedHeapHandle(const HeapHandle& handle) { |
| delayed_pq_heap_handle_ = handle; |
| } |
| |
| void TaskSource::ClearDelayedHeapHandle() { |
| delayed_pq_heap_handle_ = HeapHandle(); |
| } |
| |
| TaskSource::TaskSource(const TaskTraits& traits, |
| TaskRunner* task_runner, |
| TaskSourceExecutionMode execution_mode) |
| : traits_(traits), |
| priority_racy_(traits.priority()), |
| task_runner_(task_runner), |
| execution_mode_(execution_mode) { |
| DCHECK(task_runner_ || |
| execution_mode_ == TaskSourceExecutionMode::kParallel || |
| execution_mode_ == TaskSourceExecutionMode::kJob); |
| } |
| |
| TaskSource::~TaskSource() { |
| // If this fails, a Transaction was likely held while releasing a reference to |
| // its associated task source, which lead to its destruction. Owners of |
| // Transaction must ensure to hold onto a reference of the associated task |
| // source at least until the Transaction is released to prevent UAF. |
| lock_.AssertNotHeld(); |
| } |
| |
| TaskSource::Transaction TaskSource::BeginTransaction() { |
| return Transaction(this); |
| } |
| |
| void TaskSource::ClearForTesting() { |
| auto task = Clear(nullptr); |
| std::move(task.task).Run(); |
| } |
| |
| RegisteredTaskSource::RegisteredTaskSource() = default; |
| |
| RegisteredTaskSource::RegisteredTaskSource(std::nullptr_t) |
| : RegisteredTaskSource() {} |
| |
| RegisteredTaskSource::RegisteredTaskSource( |
| RegisteredTaskSource&& other) noexcept |
| : |
| #if DCHECK_IS_ON() |
| run_step_{std::exchange(other.run_step_, State::kInitial)}, |
| #endif // DCHECK_IS_ON() |
| task_source_{std::move(other.task_source_)}, |
| task_tracker_{std::exchange(other.task_tracker_, nullptr)} { |
| } |
| |
| RegisteredTaskSource::~RegisteredTaskSource() { |
| Unregister(); |
| } |
| |
| // static |
| RegisteredTaskSource RegisteredTaskSource::CreateForTesting( |
| scoped_refptr<TaskSource> task_source, |
| TaskTracker* task_tracker) { |
| return RegisteredTaskSource(std::move(task_source), task_tracker); |
| } |
| |
| scoped_refptr<TaskSource> RegisteredTaskSource::Unregister() { |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(run_step_, State::kInitial); |
| #endif // DCHECK_IS_ON() |
| if (task_source_ && task_tracker_) |
| return task_tracker_->UnregisterTaskSource(std::move(task_source_)); |
| return std::move(task_source_); |
| } |
| |
| RegisteredTaskSource& RegisteredTaskSource::operator=( |
| RegisteredTaskSource&& other) { |
| Unregister(); |
| #if DCHECK_IS_ON() |
| run_step_ = std::exchange(other.run_step_, State::kInitial); |
| #endif // DCHECK_IS_ON() |
| task_source_ = std::move(other.task_source_); |
| task_tracker_ = std::exchange(other.task_tracker_, nullptr); |
| return *this; |
| } |
| |
| TaskSource::RunStatus RegisteredTaskSource::WillRunTask() { |
| TaskSource::RunStatus run_status = task_source_->WillRunTask(); |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(run_step_, State::kInitial); |
| if (run_status != TaskSource::RunStatus::kDisallowed) |
| run_step_ = State::kReady; |
| #endif // DCHECK_IS_ON() |
| return run_status; |
| } |
| |
| Task RegisteredTaskSource::TakeTask(TaskSource::Transaction* transaction) { |
| DCHECK(!transaction || transaction->task_source() == get()); |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(State::kReady, run_step_); |
| #endif // DCHECK_IS_ON() |
| return task_source_->TakeTask(transaction); |
| } |
| |
| Task RegisteredTaskSource::Clear(TaskSource::Transaction* transaction) { |
| DCHECK(!transaction || transaction->task_source() == get()); |
| return task_source_->Clear(transaction); |
| } |
| |
| bool RegisteredTaskSource::DidProcessTask( |
| TaskSource::Transaction* transaction) { |
| DCHECK(!transaction || transaction->task_source() == get()); |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(State::kReady, run_step_); |
| run_step_ = State::kInitial; |
| #endif // DCHECK_IS_ON() |
| return task_source_->DidProcessTask(transaction); |
| } |
| |
| bool RegisteredTaskSource::WillReEnqueue(TimeTicks now, |
| TaskSource::Transaction* transaction) { |
| DCHECK(!transaction || transaction->task_source() == get()); |
| #if DCHECK_IS_ON() |
| DCHECK_EQ(State::kInitial, run_step_); |
| #endif // DCHECK_IS_ON() |
| return task_source_->WillReEnqueue(now, transaction); |
| } |
| |
| RegisteredTaskSource::RegisteredTaskSource( |
| scoped_refptr<TaskSource> task_source, |
| TaskTracker* task_tracker) |
| : task_source_(std::move(task_source)), task_tracker_(task_tracker) {} |
| |
| TransactionWithRegisteredTaskSource::TransactionWithRegisteredTaskSource( |
| RegisteredTaskSource task_source_in, |
| TaskSource::Transaction transaction_in) |
| : task_source(std::move(task_source_in)), |
| transaction(std::move(transaction_in)) { |
| DCHECK_EQ(task_source.get(), transaction.task_source()); |
| } |
| |
| // static: |
| TransactionWithRegisteredTaskSource |
| TransactionWithRegisteredTaskSource::FromTaskSource( |
| RegisteredTaskSource task_source_in) { |
| auto transaction = task_source_in->BeginTransaction(); |
| return TransactionWithRegisteredTaskSource(std::move(task_source_in), |
| std::move(transaction)); |
| } |
| |
| TaskSourceAndTransaction::TaskSourceAndTransaction( |
| TaskSourceAndTransaction&& other) = default; |
| |
| TaskSourceAndTransaction::~TaskSourceAndTransaction() = default; |
| |
| TaskSourceAndTransaction::TaskSourceAndTransaction( |
| scoped_refptr<TaskSource> task_source_in, |
| TaskSource::Transaction transaction_in) |
| : task_source(std::move(task_source_in)), |
| transaction(std::move(transaction_in)) { |
| DCHECK_EQ(task_source.get(), transaction.task_source()); |
| } |
| |
| // static: |
| TaskSourceAndTransaction TaskSourceAndTransaction::FromTaskSource( |
| scoped_refptr<TaskSource> task_source_in) { |
| auto transaction = task_source_in->BeginTransaction(); |
| return TaskSourceAndTransaction(std::move(task_source_in), |
| std::move(transaction)); |
| } |
| |
| } // namespace internal |
| } // namespace base |