blob: 7fa78e6f85ef748bf4884eca17b8286cb1acfbb2 [file] [log] [blame]
// Copyright 2016 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/task_scheduler/priority_queue.h"
#include <utility>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
namespace base {
namespace internal {
// A class combining a Sequence and the SequenceSortKey that determines its
// position in a PriorityQueue. Instances are only mutable via take_sequence()
// which can only be called once and renders its instance invalid after the
// call.
class PriorityQueue::SequenceAndSortKey {
public:
SequenceAndSortKey() = default;
SequenceAndSortKey(scoped_refptr<Sequence> sequence,
const SequenceSortKey& sort_key)
: sequence_(std::move(sequence)), sort_key_(sort_key) {
DCHECK(sequence_);
}
// Note: while |sequence_| should always be non-null post-move (i.e. we
// shouldn't be moving an invalid SequenceAndSortKey around), there can't be
// a DCHECK(sequence_) on moves as IntrusiveHeap moves elements on pop
// instead of overwriting them: resulting in the move of a SequenceAndSortKey
// with a null |sequence_| in Transaction::Pop()'s implementation.
SequenceAndSortKey(SequenceAndSortKey&& other) = default;
SequenceAndSortKey& operator=(SequenceAndSortKey&& other) = default;
// Extracts |sequence_| from this object. This object is invalid after this
// call.
scoped_refptr<Sequence> take_sequence() {
DCHECK(sequence_);
sequence_->ClearHeapHandle();
return std::move(sequence_);
}
// Compares this SequenceAndSortKey to |other| based on their respective
// |sort_key_|. Required by IntrusiveHeap.
bool operator<=(const SequenceAndSortKey& other) const {
return sort_key_ <= other.sort_key_;
}
// Required by IntrusiveHeap.
void SetHeapHandle(const HeapHandle& handle) {
DCHECK(sequence_);
sequence_->SetHeapHandle(handle);
}
// Required by IntrusiveHeap.
void ClearHeapHandle() {
// Ensure |sequence_| is not nullptr, which may be the case if
// take_sequence() was called before this.
if (sequence_) {
sequence_->ClearHeapHandle();
}
}
const Sequence* sequence() const { return sequence_.get(); }
const SequenceSortKey& sort_key() const { return sort_key_; }
private:
scoped_refptr<Sequence> sequence_;
SequenceSortKey sort_key_;
DISALLOW_COPY_AND_ASSIGN(SequenceAndSortKey);
};
PriorityQueue::PriorityQueue() = default;
PriorityQueue::~PriorityQueue() {
if (!is_flush_sequences_on_destroy_enabled_)
return;
while (!container_.empty()) {
scoped_refptr<Sequence> sequence = PopSequence();
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
while (!sequence_transaction.IsEmpty()) {
sequence_transaction.TakeTask();
sequence_transaction.Pop();
}
}
}
void PriorityQueue::Push(scoped_refptr<Sequence> sequence,
const SequenceSortKey& sequence_sort_key) {
container_.insert(SequenceAndSortKey(std::move(sequence), sequence_sort_key));
IncrementNumSequencesForPriority(sequence_sort_key.priority());
}
const SequenceSortKey& PriorityQueue::PeekSortKey() const {
DCHECK(!IsEmpty());
return container_.Min().sort_key();
}
scoped_refptr<Sequence> PriorityQueue::PopSequence() {
DCHECK(!IsEmpty());
// The const_cast on Min() is okay since the SequenceAndSortKey is
// transactionally being popped from |container_| right after and taking its
// Sequence does not alter its sort order.
auto& sequence_and_sort_key =
const_cast<PriorityQueue::SequenceAndSortKey&>(container_.Min());
DecrementNumSequencesForPriority(sequence_and_sort_key.sort_key().priority());
scoped_refptr<Sequence> sequence = sequence_and_sort_key.take_sequence();
container_.Pop();
return sequence;
}
bool PriorityQueue::RemoveSequence(scoped_refptr<Sequence> sequence) {
DCHECK(sequence);
if (IsEmpty())
return false;
const HeapHandle heap_handle = sequence->heap_handle();
if (!heap_handle.IsValid())
return false;
const SequenceAndSortKey& sequence_and_sort_key = container_.at(heap_handle);
DCHECK_EQ(sequence_and_sort_key.sequence(), sequence.get());
DecrementNumSequencesForPriority(sequence_and_sort_key.sort_key().priority());
container_.erase(heap_handle);
return true;
}
void PriorityQueue::UpdateSortKey(
SequenceAndTransaction sequence_and_transaction) {
DCHECK(sequence_and_transaction.sequence);
if (IsEmpty())
return;
const HeapHandle heap_handle =
sequence_and_transaction.sequence->heap_handle();
if (!heap_handle.IsValid())
return;
auto old_sort_key = container_.at(heap_handle).sort_key();
auto new_sort_key = sequence_and_transaction.transaction.GetSortKey();
DecrementNumSequencesForPriority(old_sort_key.priority());
IncrementNumSequencesForPriority(new_sort_key.priority());
container_.ChangeKey(
heap_handle,
SequenceAndSortKey(std::move(sequence_and_transaction.sequence),
new_sort_key));
}
bool PriorityQueue::IsEmpty() const {
return container_.empty();
}
size_t PriorityQueue::Size() const {
return container_.size();
}
void PriorityQueue::EnableFlushSequencesOnDestroyForTesting() {
DCHECK(!is_flush_sequences_on_destroy_enabled_);
is_flush_sequences_on_destroy_enabled_ = true;
}
void PriorityQueue::DecrementNumSequencesForPriority(TaskPriority priority) {
DCHECK_GT(num_sequences_per_priority_[static_cast<int>(priority)], 0U);
--num_sequences_per_priority_[static_cast<int>(priority)];
}
void PriorityQueue::IncrementNumSequencesForPriority(TaskPriority priority) {
++num_sequences_per_priority_[static_cast<int>(priority)];
}
} // namespace internal
} // namespace base