// 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 #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 { TaskQueue::QueueEnabledVoter::QueueEnabledVoter( scoped_refptr 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); // Voters and task queues are often stored in pairs, and the voter is often // destroyed after the queue is shut down. 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 impl, const TaskQueue::Spec& spec) : impl_(std::move(impl)), sequence_manager_(impl_->GetSequenceManagerWeakPtr()), associated_thread_((impl_->sequence_manager()) ? impl_->sequence_manager()->associated_thread() : MakeRefCounted()), default_task_runner_(impl_->CreateTaskRunner(kTaskTypeNone)), name_(impl_->GetProtoName()) {} 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); // TODO(crbug.com/1413795): Fix that some task queues get shut down more than // once. if (!impl_) { return; } if (!sequence_manager_) { TakeTaskQueueImpl().reset(); return; } sequence_manager_->UnregisterTaskQueueImpl(TakeTaskQueueImpl()); } scoped_refptr 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); DCHECK(impl_); return impl_->CreateTaskRunner(task_type); } std::unique_ptr TaskQueue::CreateQueueEnabledVoter() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return WrapUnique(new QueueEnabledVoter(this)); } bool TaskQueue::IsQueueEnabled() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->IsQueueEnabled(); } bool TaskQueue::IsEmpty() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->IsEmpty(); } size_t TaskQueue::GetNumberOfPendingTasks() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->GetNumberOfPendingTasks(); } bool TaskQueue::HasTaskToRunImmediatelyOrReadyDelayedTask() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->HasTaskToRunImmediatelyOrReadyDelayedTask(); } absl::optional TaskQueue::GetNextDesiredWakeUp() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->GetNextDesiredWakeUp(); } void TaskQueue::UpdateWakeUp(LazyNow* lazy_now) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->UpdateWakeUp(lazy_now); } void TaskQueue::SetQueuePriority(TaskQueue::QueuePriority priority) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->SetQueuePriority(priority); } TaskQueue::QueuePriority TaskQueue::GetQueuePriority() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); // TODO(crbug.com/1413795): change this to DCHECK(impl_) since task queues // should not be used after shutdown. DCHECK(impl_); return impl_->GetQueuePriority(); } void TaskQueue::AddTaskObserver(TaskObserver* task_observer) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->AddTaskObserver(task_observer); } void TaskQueue::RemoveTaskObserver(TaskObserver* task_observer) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->RemoveTaskObserver(task_observer); } void TaskQueue::InsertFence(InsertFencePosition position) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->InsertFence(position); } void TaskQueue::InsertFenceAt(TimeTicks time) { impl_->InsertFenceAt(time); } void TaskQueue::RemoveFence() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->RemoveFence(); } bool TaskQueue::HasActiveFence() { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->HasActiveFence(); } bool TaskQueue::BlockedByFence() const { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); 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); DCHECK(impl_); // |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); DCHECK(impl_); impl_->ResetThrottler(); } void TaskQueue::SetShouldReportPostedTasksWhenDisabled(bool should_report) { impl_->SetShouldReportPostedTasksWhenDisabled(should_report); } bool TaskQueue::IsOnMainThread() const { return associated_thread_->IsBoundToCurrentThread(); } std::unique_ptr 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); DCHECK(impl_); impl_->SetOnTaskStartedHandler(std::move(handler)); } void TaskQueue::SetOnTaskCompletedHandler(OnTaskCompletedHandler handler) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->SetOnTaskCompletedHandler(std::move(handler)); } std::unique_ptr TaskQueue::AddOnTaskPostedHandler(OnTaskPostedHandler handler) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); return impl_->AddOnTaskPostedHandler(std::move(handler)); } void TaskQueue::SetTaskExecutionTraceLogger(TaskExecutionTraceLogger logger) { DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK(impl_); impl_->SetTaskExecutionTraceLogger(std::move(logger)); } } // namespace sequence_manager } // namespace base