| // Copyright 2017 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/sequence_manager/task_queue.h" |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/task/sequence_manager/associated_thread_id.h" |
| #include "base/task/sequence_manager/sequence_manager_impl.h" |
| #include "base/task/sequence_manager/task_queue_impl.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/threading/thread_checker_impl.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/base_tracing.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace base { |
| namespace sequence_manager { |
| |
| namespace { |
| |
| class NullTaskRunner final : public SingleThreadTaskRunner { |
| public: |
| NullTaskRunner() {} |
| |
| bool PostDelayedTask(const Location& location, |
| OnceClosure callback, |
| TimeDelta delay) override { |
| return false; |
| } |
| |
| bool PostNonNestableDelayedTask(const Location& location, |
| OnceClosure callback, |
| TimeDelta delay) override { |
| return false; |
| } |
| |
| bool RunsTasksInCurrentSequence() const override { |
| return thread_checker_.CalledOnValidThread(); |
| } |
| |
| private: |
| // Ref-counted |
| ~NullTaskRunner() override = default; |
| |
| ThreadCheckerImpl thread_checker_; |
| }; |
| |
| // TODO(kraynov): Move NullTaskRunner from //base/test to //base. |
| scoped_refptr<SingleThreadTaskRunner> CreateNullTaskRunner() { |
| return MakeRefCounted<NullTaskRunner>(); |
| } |
| |
| } // namespace |
| |
| TaskQueue::QueueEnabledVoter::QueueEnabledVoter( |
| scoped_refptr<TaskQueue> task_queue) |
| : task_queue_(std::move(task_queue)), enabled_(true) { |
| task_queue_->AddQueueEnabledVoter(enabled_); |
| } |
| |
| TaskQueue::QueueEnabledVoter::~QueueEnabledVoter() { |
| task_queue_->RemoveQueueEnabledVoter(enabled_); |
| } |
| |
| void TaskQueue::QueueEnabledVoter::SetVoteToEnable(bool enabled) { |
| if (enabled == enabled_) |
| return; |
| enabled_ = enabled; |
| task_queue_->OnQueueEnabledVoteChanged(enabled_); |
| } |
| |
| void TaskQueue::AddQueueEnabledVoter(bool voter_is_enabled) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| ++voter_count_; |
| if (voter_is_enabled) |
| ++enabled_voter_count_; |
| } |
| |
| void TaskQueue::RemoveQueueEnabledVoter(bool voter_is_enabled) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| |
| bool was_enabled = AreAllQueueEnabledVotersEnabled(); |
| if (voter_is_enabled) { |
| --enabled_voter_count_; |
| DCHECK_GE(enabled_voter_count_, 0); |
| } |
| |
| --voter_count_; |
| DCHECK_GE(voter_count_, 0); |
| |
| bool is_enabled = AreAllQueueEnabledVotersEnabled(); |
| if (was_enabled != is_enabled) |
| impl_->SetQueueEnabled(is_enabled); |
| } |
| |
| bool TaskQueue::AreAllQueueEnabledVotersEnabled() const { |
| return enabled_voter_count_ == voter_count_; |
| } |
| |
| void TaskQueue::OnQueueEnabledVoteChanged(bool enabled) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| bool was_enabled = AreAllQueueEnabledVotersEnabled(); |
| if (enabled) { |
| ++enabled_voter_count_; |
| DCHECK_LE(enabled_voter_count_, voter_count_); |
| } else { |
| --enabled_voter_count_; |
| DCHECK_GE(enabled_voter_count_, 0); |
| } |
| |
| bool is_enabled = AreAllQueueEnabledVotersEnabled(); |
| if (was_enabled != is_enabled) |
| impl_->SetQueueEnabled(is_enabled); |
| } |
| |
| TaskQueue::TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl, |
| const TaskQueue::Spec& spec) |
| : impl_(std::move(impl)), |
| sequence_manager_(impl_ ? impl_->GetSequenceManagerWeakPtr() : nullptr), |
| associated_thread_((impl_ && impl_->sequence_manager()) |
| ? impl_->sequence_manager()->associated_thread() |
| : MakeRefCounted<internal::AssociatedThreadId>()), |
| default_task_runner_(impl_ ? impl_->CreateTaskRunner(kTaskTypeNone) |
| : CreateNullTaskRunner()), |
| name_(impl_ ? impl_->GetProtoName() : QueueName::UNKNOWN_TQ) {} |
| |
| TaskQueue::~TaskQueue() { |
| ShutdownTaskQueueGracefully(); |
| } |
| |
| void TaskQueue::ShutdownTaskQueueGracefully() { |
| // scoped_refptr guarantees us that this object isn't used. |
| if (!impl_) |
| return; |
| if (impl_->IsUnregistered()) |
| return; |
| |
| // If we've not been unregistered then this must occur on the main thread. |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| impl_->ResetThrottler(); |
| impl_->sequence_manager()->ShutdownTaskQueueGracefully(TakeTaskQueueImpl()); |
| } |
| |
| TaskQueue::TaskTiming::TaskTiming(bool has_wall_time, bool has_thread_time) |
| : has_wall_time_(has_wall_time), has_thread_time_(has_thread_time) {} |
| |
| void TaskQueue::TaskTiming::RecordTaskStart(LazyNow* now) { |
| DCHECK_EQ(State::NotStarted, state_); |
| state_ = State::Running; |
| |
| if (has_wall_time()) |
| start_time_ = now->Now(); |
| if (has_thread_time()) |
| start_thread_time_ = base::ThreadTicks::Now(); |
| } |
| |
| void TaskQueue::TaskTiming::RecordTaskEnd(LazyNow* now) { |
| DCHECK(state_ == State::Running || state_ == State::Finished); |
| if (state_ == State::Finished) |
| return; |
| state_ = State::Finished; |
| |
| if (has_wall_time()) |
| end_time_ = now->Now(); |
| if (has_thread_time()) |
| end_thread_time_ = base::ThreadTicks::Now(); |
| } |
| |
| void TaskQueue::ShutdownTaskQueue() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| if (!sequence_manager_) { |
| TakeTaskQueueImpl().reset(); |
| return; |
| } |
| sequence_manager_->UnregisterTaskQueueImpl(TakeTaskQueueImpl()); |
| } |
| |
| scoped_refptr<SingleThreadTaskRunner> TaskQueue::CreateTaskRunner( |
| TaskType task_type) { |
| // We only need to lock if we're not on the main thread. |
| base::internal::CheckedAutoLockMaybe lock(IsOnMainThread() ? &impl_lock_ |
| : nullptr); |
| if (!impl_) |
| return CreateNullTaskRunner(); |
| return impl_->CreateTaskRunner(task_type); |
| } |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> |
| TaskQueue::CreateQueueEnabledVoter() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return nullptr; |
| return WrapUnique(new QueueEnabledVoter(this)); |
| } |
| |
| bool TaskQueue::IsQueueEnabled() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return false; |
| return impl_->IsQueueEnabled(); |
| } |
| |
| bool TaskQueue::IsEmpty() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return true; |
| return impl_->IsEmpty(); |
| } |
| |
| size_t TaskQueue::GetNumberOfPendingTasks() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return 0; |
| return impl_->GetNumberOfPendingTasks(); |
| } |
| |
| bool TaskQueue::HasTaskToRunImmediatelyOrReadyDelayedTask() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return false; |
| return impl_->HasTaskToRunImmediatelyOrReadyDelayedTask(); |
| } |
| |
| absl::optional<WakeUp> TaskQueue::GetNextDesiredWakeUp() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return absl::nullopt; |
| return impl_->GetNextDesiredWakeUp(); |
| } |
| |
| void TaskQueue::UpdateWakeUp(LazyNow* lazy_now) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->UpdateWakeUp(lazy_now); |
| } |
| |
| void TaskQueue::SetQueuePriority(TaskQueue::QueuePriority priority) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->SetQueuePriority(priority); |
| } |
| |
| TaskQueue::QueuePriority TaskQueue::GetQueuePriority() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return TaskQueue::QueuePriority::kLowPriority; |
| return impl_->GetQueuePriority(); |
| } |
| |
| void TaskQueue::AddTaskObserver(TaskObserver* task_observer) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->AddTaskObserver(task_observer); |
| } |
| |
| void TaskQueue::RemoveTaskObserver(TaskObserver* task_observer) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->RemoveTaskObserver(task_observer); |
| } |
| |
| void TaskQueue::InsertFence(InsertFencePosition position) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->InsertFence(position); |
| } |
| |
| void TaskQueue::InsertFenceAt(TimeTicks time) { |
| impl_->InsertFenceAt(time); |
| } |
| |
| void TaskQueue::RemoveFence() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->RemoveFence(); |
| } |
| |
| bool TaskQueue::HasActiveFence() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return false; |
| return impl_->HasActiveFence(); |
| } |
| |
| bool TaskQueue::BlockedByFence() const { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return false; |
| return impl_->BlockedByFence(); |
| } |
| |
| const char* TaskQueue::GetName() const { |
| return perfetto::protos::pbzero::SequenceManagerTask::QueueName_Name(name_); |
| } |
| |
| void TaskQueue::WriteIntoTrace(perfetto::TracedValue context) const { |
| auto dict = std::move(context).WriteDictionary(); |
| dict.Add("name", name_); |
| } |
| |
| void TaskQueue::SetThrottler(Throttler* throttler) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| |
| // |throttler| is guaranteed to outlive TaskQueue and TaskQueueImpl lifecycle |
| // is controlled by |this|. |
| impl_->SetThrottler(throttler); |
| } |
| |
| void TaskQueue::ResetThrottler() { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| impl_->ResetThrottler(); |
| } |
| |
| void TaskQueue::SetShouldReportPostedTasksWhenDisabled(bool should_report) { |
| impl_->SetShouldReportPostedTasksWhenDisabled(should_report); |
| } |
| |
| bool TaskQueue::IsOnMainThread() const { |
| return associated_thread_->IsBoundToCurrentThread(); |
| } |
| |
| std::unique_ptr<internal::TaskQueueImpl> TaskQueue::TakeTaskQueueImpl() { |
| base::internal::CheckedAutoLock lock(impl_lock_); |
| DCHECK(impl_); |
| return std::move(impl_); |
| } |
| |
| void TaskQueue::SetOnTaskStartedHandler(OnTaskStartedHandler handler) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| |
| impl_->SetOnTaskStartedHandler(std::move(handler)); |
| } |
| |
| void TaskQueue::SetOnTaskCompletedHandler(OnTaskCompletedHandler handler) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| |
| impl_->SetOnTaskCompletedHandler(std::move(handler)); |
| } |
| |
| std::unique_ptr<TaskQueue::OnTaskPostedCallbackHandle> |
| TaskQueue::AddOnTaskPostedHandler(OnTaskPostedHandler handler) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return nullptr; |
| |
| return impl_->AddOnTaskPostedHandler(std::move(handler)); |
| } |
| |
| void TaskQueue::SetTaskExecutionTraceLogger(TaskExecutionTraceLogger logger) { |
| DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); |
| if (!impl_) |
| return; |
| |
| impl_->SetTaskExecutionTraceLogger(std::move(logger)); |
| } |
| |
| } // namespace sequence_manager |
| } // namespace base |