325 lines
9.9 KiB
C++
325 lines
9.9 KiB
C++
// 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 {
|
|
|
|
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);
|
|
// 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<internal::TaskQueueImpl> impl,
|
|
const TaskQueue::Spec& spec)
|
|
: impl_(std::move(impl)),
|
|
sequence_manager_(impl_->GetSequenceManagerWeakPtr()),
|
|
associated_thread_((impl_->sequence_manager())
|
|
? impl_->sequence_manager()->associated_thread()
|
|
: MakeRefCounted<internal::AssociatedThreadId>()),
|
|
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<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);
|
|
DCHECK(impl_);
|
|
return impl_->CreateTaskRunner(task_type);
|
|
}
|
|
|
|
std::unique_ptr<TaskQueue::QueueEnabledVoter>
|
|
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<WakeUp> 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<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);
|
|
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::OnTaskPostedCallbackHandle>
|
|
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
|