207 lines
6.7 KiB
C++
207 lines
6.7 KiB
C++
// Copyright 2016 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/thread_pool/priority_queue.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/types/cxx23_to_underlying.h"
|
|
|
|
namespace base {
|
|
namespace internal {
|
|
|
|
// A class combining a TaskSource and the TaskSourceSortKey that determines its
|
|
// position in a PriorityQueue. Instances are only mutable via
|
|
// take_task_source() which can only be called once and renders its instance
|
|
// invalid after the call.
|
|
class PriorityQueue::TaskSourceAndSortKey {
|
|
public:
|
|
TaskSourceAndSortKey() = default;
|
|
TaskSourceAndSortKey(RegisteredTaskSource task_source,
|
|
const TaskSourceSortKey& sort_key)
|
|
: task_source_(std::move(task_source)), sort_key_(sort_key) {
|
|
DCHECK(task_source_);
|
|
}
|
|
TaskSourceAndSortKey(const TaskSourceAndSortKey&) = delete;
|
|
TaskSourceAndSortKey& operator=(const TaskSourceAndSortKey&) = delete;
|
|
|
|
// Note: while |task_source_| should always be non-null post-move (i.e. we
|
|
// shouldn't be moving an invalid TaskSourceAndSortKey around), there can't be
|
|
// a DCHECK(task_source_) on moves as IntrusiveHeap moves elements on pop
|
|
// instead of overwriting them: resulting in the move of a
|
|
// TaskSourceAndSortKey with a null |task_source_| in Transaction::Pop()'s
|
|
// implementation.
|
|
TaskSourceAndSortKey(TaskSourceAndSortKey&& other) = default;
|
|
TaskSourceAndSortKey& operator=(TaskSourceAndSortKey&& other) = default;
|
|
|
|
// Extracts |task_source_| from this object. This object is invalid after this
|
|
// call.
|
|
RegisteredTaskSource take_task_source() {
|
|
DCHECK(task_source_);
|
|
task_source_->ClearImmediateHeapHandle();
|
|
return std::move(task_source_);
|
|
}
|
|
|
|
// Compares this TaskSourceAndSortKey to |other| based on their respective
|
|
// |sort_key_|. Used for a max-heap.
|
|
bool operator<(const TaskSourceAndSortKey& other) const {
|
|
return sort_key_ < other.sort_key_;
|
|
}
|
|
|
|
// Required by IntrusiveHeap.
|
|
void SetHeapHandle(const HeapHandle& handle) {
|
|
DCHECK(task_source_);
|
|
task_source_->SetImmediateHeapHandle(handle);
|
|
}
|
|
|
|
// Required by IntrusiveHeap.
|
|
void ClearHeapHandle() {
|
|
// Ensure |task_source_| is not nullptr, which may be the case if
|
|
// take_task_source() was called before this.
|
|
if (task_source_)
|
|
task_source_->ClearImmediateHeapHandle();
|
|
}
|
|
|
|
// Required by IntrusiveHeap.
|
|
HeapHandle GetHeapHandle() const {
|
|
if (task_source_)
|
|
return task_source_->GetImmediateHeapHandle();
|
|
return HeapHandle::Invalid();
|
|
}
|
|
|
|
const RegisteredTaskSource& task_source() const { return task_source_; }
|
|
RegisteredTaskSource& task_source() { return task_source_; }
|
|
|
|
const TaskSourceSortKey& sort_key() const { return sort_key_; }
|
|
|
|
private:
|
|
RegisteredTaskSource task_source_;
|
|
TaskSourceSortKey sort_key_;
|
|
};
|
|
|
|
PriorityQueue::PriorityQueue() = default;
|
|
|
|
PriorityQueue::~PriorityQueue() {
|
|
if (!is_flush_task_sources_on_destroy_enabled_)
|
|
return;
|
|
|
|
while (!container_.empty()) {
|
|
auto task_source = PopTaskSource();
|
|
auto task = task_source.Clear();
|
|
std::move(task.task).Run();
|
|
}
|
|
}
|
|
|
|
PriorityQueue& PriorityQueue::operator=(PriorityQueue&& other) = default;
|
|
|
|
void PriorityQueue::Push(RegisteredTaskSource task_source,
|
|
TaskSourceSortKey task_source_sort_key) {
|
|
container_.insert(
|
|
TaskSourceAndSortKey(std::move(task_source), task_source_sort_key));
|
|
IncrementNumTaskSourcesForPriority(task_source_sort_key.priority());
|
|
}
|
|
|
|
const TaskSourceSortKey& PriorityQueue::PeekSortKey() const {
|
|
DCHECK(!IsEmpty());
|
|
return container_.top().sort_key();
|
|
}
|
|
|
|
RegisteredTaskSource& PriorityQueue::PeekTaskSource() const {
|
|
DCHECK(!IsEmpty());
|
|
|
|
// The const_cast on Min() is okay since modifying the TaskSource cannot alter
|
|
// the sort order of TaskSourceAndSortKey.
|
|
auto& task_source_and_sort_key =
|
|
const_cast<PriorityQueue::TaskSourceAndSortKey&>(container_.top());
|
|
return task_source_and_sort_key.task_source();
|
|
}
|
|
|
|
RegisteredTaskSource PriorityQueue::PopTaskSource() {
|
|
DCHECK(!IsEmpty());
|
|
|
|
// The const_cast on Min() is okay since the TaskSourceAndSortKey is
|
|
// transactionally being popped from |container_| right after and taking its
|
|
// TaskSource does not alter its sort order.
|
|
auto& task_source_and_sort_key =
|
|
const_cast<TaskSourceAndSortKey&>(container_.top());
|
|
DecrementNumTaskSourcesForPriority(
|
|
task_source_and_sort_key.sort_key().priority());
|
|
RegisteredTaskSource task_source =
|
|
task_source_and_sort_key.take_task_source();
|
|
container_.pop();
|
|
return task_source;
|
|
}
|
|
|
|
RegisteredTaskSource PriorityQueue::RemoveTaskSource(
|
|
const TaskSource& task_source) {
|
|
if (IsEmpty())
|
|
return nullptr;
|
|
|
|
const HeapHandle heap_handle = task_source.immediate_heap_handle();
|
|
if (!heap_handle.IsValid())
|
|
return nullptr;
|
|
|
|
TaskSourceAndSortKey& task_source_and_sort_key =
|
|
const_cast<PriorityQueue::TaskSourceAndSortKey&>(
|
|
container_.at(heap_handle));
|
|
DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source);
|
|
RegisteredTaskSource registered_task_source =
|
|
task_source_and_sort_key.take_task_source();
|
|
|
|
DecrementNumTaskSourcesForPriority(
|
|
task_source_and_sort_key.sort_key().priority());
|
|
container_.erase(heap_handle);
|
|
return registered_task_source;
|
|
}
|
|
|
|
void PriorityQueue::UpdateSortKey(const TaskSource& task_source,
|
|
TaskSourceSortKey sort_key) {
|
|
if (IsEmpty())
|
|
return;
|
|
|
|
const HeapHandle heap_handle = task_source.immediate_heap_handle();
|
|
if (!heap_handle.IsValid())
|
|
return;
|
|
|
|
auto old_sort_key = container_.at(heap_handle).sort_key();
|
|
auto registered_task_source =
|
|
const_cast<PriorityQueue::TaskSourceAndSortKey&>(
|
|
container_.at(heap_handle))
|
|
.take_task_source();
|
|
|
|
DecrementNumTaskSourcesForPriority(old_sort_key.priority());
|
|
IncrementNumTaskSourcesForPriority(sort_key.priority());
|
|
|
|
container_.Replace(
|
|
heap_handle,
|
|
TaskSourceAndSortKey(std::move(registered_task_source), sort_key));
|
|
}
|
|
|
|
bool PriorityQueue::IsEmpty() const {
|
|
return container_.empty();
|
|
}
|
|
|
|
size_t PriorityQueue::Size() const {
|
|
return container_.size();
|
|
}
|
|
|
|
void PriorityQueue::EnableFlushTaskSourcesOnDestroyForTesting() {
|
|
DCHECK(!is_flush_task_sources_on_destroy_enabled_);
|
|
is_flush_task_sources_on_destroy_enabled_ = true;
|
|
}
|
|
|
|
void PriorityQueue::DecrementNumTaskSourcesForPriority(TaskPriority priority) {
|
|
DCHECK_GT(num_task_sources_per_priority_[base::to_underlying(priority)], 0U);
|
|
--num_task_sources_per_priority_[base::to_underlying(priority)];
|
|
}
|
|
|
|
void PriorityQueue::IncrementNumTaskSourcesForPriority(TaskPriority priority) {
|
|
++num_task_sources_per_priority_[base::to_underlying(priority)];
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace base
|