blob: e28f4814c7dfe35d7bc87751023bd015e5ed214b [file] [log] [blame]
// 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 "platform/scheduler/base/work_queue.h"
#include "platform/scheduler/base/work_queue_sets.h"
namespace blink {
namespace scheduler {
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(base::TimeTicks now,
base::trace_event::TracedValue* state) const {
for (const TaskQueueImpl::Task& task : work_queue_) {
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 (work_queue_.empty())
return nullptr;
return &work_queue_.front();
}
const TaskQueueImpl::Task* WorkQueue::GetBackTask() const {
if (work_queue_.empty())
return nullptr;
return &work_queue_.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 work_queue_.empty() || work_queue_.front().enqueue_order() >= fence_;
}
bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const {
if (work_queue_.empty() || BlockedByFence())
return false;
// Quick sanity check.
DCHECK_LE(work_queue_.front().enqueue_order(),
work_queue_.back().enqueue_order())
<< task_queue_->GetName() << " : " << work_queue_sets_->GetName() << " : "
<< name_;
*enqueue_order = work_queue_.front().enqueue_order();
return true;
}
void WorkQueue::Push(TaskQueueImpl::Task task) {
bool was_empty = work_queue_.empty();
#ifndef NDEBUG
DCHECK(task.enqueue_order_set());
#endif
// Amoritized O(1).
work_queue_.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::ReloadEmptyImmediateQueue() {
DCHECK(work_queue_.empty());
work_queue_ = task_queue_->TakeImmediateIncomingQueue();
if (work_queue_.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(!work_queue_.empty());
// Skip over canceled tasks, except for the last one since we always return
// something.
while (work_queue_.size() > 1u) {
if (work_queue_.front().task.IsCancelled()) {
work_queue_.pop_front();
} else {
break;
}
}
TaskQueueImpl::Task pending_task = work_queue_.TakeFirst();
// NB immediate tasks have a different pipeline to delayed ones.
if (queue_type_ == QueueType::kImmediate && work_queue_.empty()) {
// Short-circuit the queue reload so that OnPopQueue does the right thing.
work_queue_ = task_queue_->TakeImmediateIncomingQueue();
}
// OnPopQueue calls GetFrontTaskEnqueueOrder which checks BlockedByFence() so
// we don't need to here.
work_queue_sets_->OnPopQueue(this);
task_queue_->TraceQueueSize();
return pending_task;
}
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::InsertFence(EnqueueOrder fence) {
DCHECK_NE(fence, 0u);
DCHECK(fence >= fence_ || fence == 1u);
bool was_blocked_by_fence = BlockedByFence();
fence_ = fence;
// Moving the fence forward may unblock some tasks.
if (work_queue_sets_ && !work_queue_.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_ = 0;
if (work_queue_sets_ && !work_queue_.empty() && was_blocked_by_fence) {
work_queue_sets_->OnTaskPushedToEmptyQueue(this);
return true;
}
return false;
}
bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const {
DCHECK(!work_queue_.empty());
DCHECK(!other_queue->work_queue_.empty());
EnqueueOrder enqueue_order = 0;
EnqueueOrder other_enqueue_order = 0;
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 (work_queue_.empty())
return;
work_queue_.pop_front();
}
} // namespace internal
} // namespace scheduler
} // namespace blink