blob: e56fc82e0b3a087f9e2369607ea87d9bd56feef4 [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 "base/task/sequence_manager/work_queue_sets.h"
#include "base/logging.h"
namespace base {
namespace sequence_manager {
namespace internal {
WorkQueueSets::WorkQueueSets(size_t num_sets, const char* name)
: work_queue_heaps_(num_sets), name_(name) {}
WorkQueueSets::~WorkQueueSets() = default;
void WorkQueueSets::AddQueue(WorkQueue* work_queue, size_t set_index) {
DCHECK(!work_queue->work_queue_sets());
DCHECK_LT(set_index, work_queue_heaps_.size());
EnqueueOrder enqueue_order;
bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
work_queue->AssignToWorkQueueSets(this);
work_queue->AssignSetIndex(set_index);
if (!has_enqueue_order)
return;
work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
}
void WorkQueueSets::RemoveQueue(WorkQueue* work_queue) {
DCHECK_EQ(this, work_queue->work_queue_sets());
work_queue->AssignToWorkQueueSets(nullptr);
HeapHandle heap_handle = work_queue->heap_handle();
if (!heap_handle.IsValid())
return;
size_t set_index = work_queue->work_queue_set_index();
DCHECK_LT(set_index, work_queue_heaps_.size());
work_queue_heaps_[set_index].erase(heap_handle);
}
void WorkQueueSets::ChangeSetIndex(WorkQueue* work_queue, size_t set_index) {
DCHECK_EQ(this, work_queue->work_queue_sets());
DCHECK_LT(set_index, work_queue_heaps_.size());
EnqueueOrder enqueue_order;
bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
size_t old_set = work_queue->work_queue_set_index();
DCHECK_LT(old_set, work_queue_heaps_.size());
DCHECK_NE(old_set, set_index);
work_queue->AssignSetIndex(set_index);
if (!has_enqueue_order)
return;
work_queue_heaps_[old_set].erase(work_queue->heap_handle());
work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
}
void WorkQueueSets::OnFrontTaskChanged(WorkQueue* work_queue) {
EnqueueOrder enqueue_order;
bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
DCHECK(has_enqueue_order);
size_t set = work_queue->work_queue_set_index();
work_queue_heaps_[set].ChangeKey(work_queue->heap_handle(),
{enqueue_order, work_queue});
}
void WorkQueueSets::OnTaskPushedToEmptyQueue(WorkQueue* work_queue) {
// NOTE if this function changes, we need to keep |WorkQueueSets::AddQueue| in
// sync.
DCHECK_EQ(this, work_queue->work_queue_sets());
EnqueueOrder enqueue_order;
bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
DCHECK(has_enqueue_order);
size_t set_index = work_queue->work_queue_set_index();
DCHECK_LT(set_index, work_queue_heaps_.size())
<< " set_index = " << set_index;
// |work_queue| should not be in work_queue_heaps_[set_index].
DCHECK(!work_queue->heap_handle().IsValid());
work_queue_heaps_[set_index].insert({enqueue_order, work_queue});
}
void WorkQueueSets::OnPopQueue(WorkQueue* work_queue) {
// Assume that |work_queue| contains the lowest enqueue_order.
size_t set_index = work_queue->work_queue_set_index();
DCHECK_EQ(this, work_queue->work_queue_sets());
DCHECK_LT(set_index, work_queue_heaps_.size());
DCHECK(!work_queue_heaps_[set_index].empty()) << " set_index = " << set_index;
DCHECK_EQ(work_queue_heaps_[set_index].Min().value, work_queue)
<< " set_index = " << set_index;
DCHECK(work_queue->heap_handle().IsValid());
EnqueueOrder enqueue_order;
if (work_queue->GetFrontTaskEnqueueOrder(&enqueue_order)) {
// O(log n)
work_queue_heaps_[set_index].ReplaceMin({enqueue_order, work_queue});
} else {
// O(log n)
work_queue_heaps_[set_index].Pop();
DCHECK(work_queue_heaps_[set_index].empty() ||
work_queue_heaps_[set_index].Min().value != work_queue);
}
}
void WorkQueueSets::OnQueueBlocked(WorkQueue* work_queue) {
DCHECK_EQ(this, work_queue->work_queue_sets());
HeapHandle heap_handle = work_queue->heap_handle();
if (!heap_handle.IsValid())
return;
size_t set_index = work_queue->work_queue_set_index();
DCHECK_LT(set_index, work_queue_heaps_.size());
work_queue_heaps_[set_index].erase(heap_handle);
}
bool WorkQueueSets::GetOldestQueueInSet(size_t set_index,
WorkQueue** out_work_queue) const {
DCHECK_LT(set_index, work_queue_heaps_.size());
if (work_queue_heaps_[set_index].empty())
return false;
*out_work_queue = work_queue_heaps_[set_index].Min().value;
DCHECK_EQ(set_index, (*out_work_queue)->work_queue_set_index());
DCHECK((*out_work_queue)->heap_handle().IsValid());
return true;
}
bool WorkQueueSets::GetOldestQueueAndEnqueueOrderInSet(
size_t set_index,
WorkQueue** out_work_queue,
EnqueueOrder* out_enqueue_order) const {
DCHECK_LT(set_index, work_queue_heaps_.size());
if (work_queue_heaps_[set_index].empty())
return false;
const OldestTaskEnqueueOrder& oldest = work_queue_heaps_[set_index].Min();
*out_work_queue = oldest.value;
*out_enqueue_order = oldest.key;
EnqueueOrder enqueue_order;
DCHECK(oldest.value->GetFrontTaskEnqueueOrder(&enqueue_order) &&
oldest.key == enqueue_order);
return true;
}
bool WorkQueueSets::IsSetEmpty(size_t set_index) const {
DCHECK_LT(set_index, work_queue_heaps_.size())
<< " set_index = " << set_index;
return work_queue_heaps_[set_index].empty();
}
#if DCHECK_IS_ON() || !defined(NDEBUG)
bool WorkQueueSets::ContainsWorkQueueForTest(
const WorkQueue* work_queue) const {
EnqueueOrder enqueue_order;
bool has_enqueue_order = work_queue->GetFrontTaskEnqueueOrder(&enqueue_order);
for (const IntrusiveHeap<OldestTaskEnqueueOrder>& heap : work_queue_heaps_) {
for (const OldestTaskEnqueueOrder& heap_value_pair : heap) {
if (heap_value_pair.value == work_queue) {
DCHECK(has_enqueue_order);
DCHECK_EQ(heap_value_pair.key, enqueue_order);
DCHECK_EQ(this, work_queue->work_queue_sets());
return true;
}
}
}
if (work_queue->work_queue_sets() == this) {
DCHECK(!has_enqueue_order);
return true;
}
return false;
}
#endif
} // namespace internal
} // namespace sequence_manager
} // namespace base