mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
237 lines
7.0 KiB
C++
237 lines
7.0 KiB
C++
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// 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/work_queue.h"
|
|
|
|
#include "base/task/sequence_manager/work_queue_sets.h"
|
|
|
|
namespace base {
|
|
namespace sequence_manager {
|
|
namespace internal {
|
|
|
|
WorkQueue::WorkQueue(TaskQueueImpl* task_queue,
|
|
const char* name,
|
|
QueueType queue_type)
|
|
: task_queue_(task_queue), name_(name), queue_type_(queue_type) {}
|
|
|
|
void WorkQueue::AsValueInto(TimeTicks now,
|
|
trace_event::TracedValue* state) const {
|
|
for (const TaskQueueImpl::Task& task : tasks_) {
|
|
TaskQueueImpl::TaskAsValueInto(task, now, state);
|
|
}
|
|
}
|
|
|
|
WorkQueue::~WorkQueue() {
|
|
DCHECK(!work_queue_sets_) << task_queue_->GetName() << " : "
|
|
<< work_queue_sets_->GetName() << " : " << name_;
|
|
}
|
|
|
|
const TaskQueueImpl::Task* WorkQueue::GetFrontTask() const {
|
|
if (tasks_.empty())
|
|
return nullptr;
|
|
return &tasks_.front();
|
|
}
|
|
|
|
const TaskQueueImpl::Task* WorkQueue::GetBackTask() const {
|
|
if (tasks_.empty())
|
|
return nullptr;
|
|
return &tasks_.back();
|
|
}
|
|
|
|
bool WorkQueue::BlockedByFence() const {
|
|
if (!fence_)
|
|
return false;
|
|
|
|
// If the queue is empty then any future tasks will have a higher enqueue
|
|
// order and will be blocked. The queue is also blocked if the head is past
|
|
// the fence.
|
|
return tasks_.empty() || tasks_.front().enqueue_order() >= fence_;
|
|
}
|
|
|
|
bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const {
|
|
if (tasks_.empty() || BlockedByFence())
|
|
return false;
|
|
// Quick sanity check.
|
|
DCHECK_LE(tasks_.front().enqueue_order(), tasks_.back().enqueue_order())
|
|
<< task_queue_->GetName() << " : " << work_queue_sets_->GetName() << " : "
|
|
<< name_;
|
|
*enqueue_order = tasks_.front().enqueue_order();
|
|
return true;
|
|
}
|
|
|
|
void WorkQueue::Push(TaskQueueImpl::Task task) {
|
|
bool was_empty = tasks_.empty();
|
|
#ifndef NDEBUG
|
|
DCHECK(task.enqueue_order_set());
|
|
#endif
|
|
|
|
// Make sure the |enqueue_order()| is monotonically increasing.
|
|
DCHECK(was_empty || tasks_.rbegin()->enqueue_order() < task.enqueue_order());
|
|
|
|
// Amoritized O(1).
|
|
tasks_.push_back(std::move(task));
|
|
|
|
if (!was_empty)
|
|
return;
|
|
|
|
// If we hit the fence, pretend to WorkQueueSets that we're empty.
|
|
if (work_queue_sets_ && !BlockedByFence())
|
|
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
|
|
}
|
|
|
|
void WorkQueue::PushNonNestableTaskToFront(TaskQueueImpl::Task task) {
|
|
DCHECK(task.nestable == Nestable::kNonNestable);
|
|
|
|
bool was_empty = tasks_.empty();
|
|
bool was_blocked = BlockedByFence();
|
|
#ifndef NDEBUG
|
|
DCHECK(task.enqueue_order_set());
|
|
#endif
|
|
|
|
if (!was_empty) {
|
|
// Make sure the |enqueue_order()| is monotonically increasing.
|
|
DCHECK_LE(task.enqueue_order(), tasks_.front().enqueue_order())
|
|
<< task_queue_->GetName() << " : " << work_queue_sets_->GetName()
|
|
<< " : " << name_;
|
|
}
|
|
|
|
// Amoritized O(1).
|
|
tasks_.push_front(std::move(task));
|
|
|
|
if (!work_queue_sets_)
|
|
return;
|
|
|
|
// Pretend to WorkQueueSets that nothing has changed if we're blocked.
|
|
if (BlockedByFence())
|
|
return;
|
|
|
|
// Pushing task to front may unblock the fence.
|
|
if (was_empty || was_blocked) {
|
|
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
|
|
} else {
|
|
work_queue_sets_->OnFrontTaskChanged(this);
|
|
}
|
|
}
|
|
|
|
void WorkQueue::ReloadEmptyImmediateQueue() {
|
|
DCHECK(tasks_.empty());
|
|
|
|
task_queue_->ReloadEmptyImmediateQueue(&tasks_);
|
|
if (tasks_.empty())
|
|
return;
|
|
|
|
// If we hit the fence, pretend to WorkQueueSets that we're empty.
|
|
if (work_queue_sets_ && !BlockedByFence())
|
|
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
|
|
}
|
|
|
|
TaskQueueImpl::Task WorkQueue::TakeTaskFromWorkQueue() {
|
|
DCHECK(work_queue_sets_);
|
|
DCHECK(!tasks_.empty());
|
|
|
|
TaskQueueImpl::Task pending_task = std::move(tasks_.front());
|
|
tasks_.pop_front();
|
|
// NB immediate tasks have a different pipeline to delayed ones.
|
|
if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
|
|
// Short-circuit the queue reload so that OnPopQueue does the right thing.
|
|
task_queue_->ReloadEmptyImmediateQueue(&tasks_);
|
|
}
|
|
|
|
// OnPopQueue calls GetFrontTaskEnqueueOrder which checks BlockedByFence() so
|
|
// we don't need to here.
|
|
work_queue_sets_->OnPopQueue(this);
|
|
task_queue_->TraceQueueSize();
|
|
return pending_task;
|
|
}
|
|
|
|
bool WorkQueue::RemoveAllCanceledTasksFromFront() {
|
|
DCHECK(work_queue_sets_);
|
|
bool task_removed = false;
|
|
while (!tasks_.empty() &&
|
|
(!tasks_.front().task || tasks_.front().task.IsCancelled())) {
|
|
tasks_.pop_front();
|
|
task_removed = true;
|
|
}
|
|
if (task_removed) {
|
|
// NB immediate tasks have a different pipeline to delayed ones.
|
|
if (queue_type_ == QueueType::kImmediate && tasks_.empty()) {
|
|
// Short-circuit the queue reload so that OnPopQueue does the right thing.
|
|
task_queue_->ReloadEmptyImmediateQueue(&tasks_);
|
|
}
|
|
work_queue_sets_->OnPopQueue(this);
|
|
task_queue_->TraceQueueSize();
|
|
}
|
|
return task_removed;
|
|
}
|
|
|
|
void WorkQueue::AssignToWorkQueueSets(WorkQueueSets* work_queue_sets) {
|
|
work_queue_sets_ = work_queue_sets;
|
|
}
|
|
|
|
void WorkQueue::AssignSetIndex(size_t work_queue_set_index) {
|
|
work_queue_set_index_ = work_queue_set_index;
|
|
}
|
|
|
|
bool WorkQueue::InsertFenceImpl(EnqueueOrder fence) {
|
|
DCHECK_NE(fence, 0u);
|
|
DCHECK(fence >= fence_ || fence == EnqueueOrder::blocking_fence());
|
|
bool was_blocked_by_fence = BlockedByFence();
|
|
fence_ = fence;
|
|
return was_blocked_by_fence;
|
|
}
|
|
|
|
void WorkQueue::InsertFenceSilently(EnqueueOrder fence) {
|
|
// Ensure that there is no fence present or a new one blocks queue completely.
|
|
DCHECK(!fence_ || fence_ == EnqueueOrder::blocking_fence());
|
|
InsertFenceImpl(fence);
|
|
}
|
|
|
|
bool WorkQueue::InsertFence(EnqueueOrder fence) {
|
|
bool was_blocked_by_fence = InsertFenceImpl(fence);
|
|
|
|
// Moving the fence forward may unblock some tasks.
|
|
if (work_queue_sets_ && !tasks_.empty() && was_blocked_by_fence &&
|
|
!BlockedByFence()) {
|
|
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
|
|
return true;
|
|
}
|
|
// Fence insertion may have blocked all tasks in this work queue.
|
|
if (BlockedByFence())
|
|
work_queue_sets_->OnQueueBlocked(this);
|
|
return false;
|
|
}
|
|
|
|
bool WorkQueue::RemoveFence() {
|
|
bool was_blocked_by_fence = BlockedByFence();
|
|
fence_ = EnqueueOrder::none();
|
|
if (work_queue_sets_ && !tasks_.empty() && was_blocked_by_fence) {
|
|
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const {
|
|
DCHECK(!tasks_.empty());
|
|
DCHECK(!other_queue->tasks_.empty());
|
|
EnqueueOrder enqueue_order;
|
|
EnqueueOrder other_enqueue_order;
|
|
bool have_task = GetFrontTaskEnqueueOrder(&enqueue_order);
|
|
bool have_other_task =
|
|
other_queue->GetFrontTaskEnqueueOrder(&other_enqueue_order);
|
|
DCHECK(have_task);
|
|
DCHECK(have_other_task);
|
|
return enqueue_order < other_enqueue_order;
|
|
}
|
|
|
|
void WorkQueue::PopTaskForTesting() {
|
|
if (tasks_.empty())
|
|
return;
|
|
tasks_.pop_front();
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace sequence_manager
|
|
} // namespace base
|