Commit 9c9ce507 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

Create base/containers/intrusive_heap.

This lifts the base/task/intrusive_heap container to base/containers, and
makes it fully STL-like.

Change-Id: I511ca1e675d37b46ec4049663c564ce490706e54
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1739411
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarAlbert J. Wong <ajwong@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#689656}
parent 5afdcbdb
...@@ -200,6 +200,8 @@ jumbo_component("base") { ...@@ -200,6 +200,8 @@ jumbo_component("base") {
"containers/flat_set.h", "containers/flat_set.h",
"containers/flat_tree.h", "containers/flat_tree.h",
"containers/id_map.h", "containers/id_map.h",
"containers/intrusive_heap.cc",
"containers/intrusive_heap.h",
"containers/linked_list.h", "containers/linked_list.h",
"containers/mru_cache.h", "containers/mru_cache.h",
"containers/small_map.h", "containers/small_map.h",
...@@ -2491,6 +2493,7 @@ test("base_unittests") { ...@@ -2491,6 +2493,7 @@ test("base_unittests") {
"containers/flat_set_unittest.cc", "containers/flat_set_unittest.cc",
"containers/flat_tree_unittest.cc", "containers/flat_tree_unittest.cc",
"containers/id_map_unittest.cc", "containers/id_map_unittest.cc",
"containers/intrusive_heap_unittest.cc",
"containers/linked_list_unittest.cc", "containers/linked_list_unittest.cc",
"containers/mru_cache_unittest.cc", "containers/mru_cache_unittest.cc",
"containers/small_map_unittest.cc", "containers/small_map_unittest.cc",
...@@ -2674,7 +2677,6 @@ test("base_unittests") { ...@@ -2674,7 +2677,6 @@ test("base_unittests") {
"system/system_monitor_unittest.cc", "system/system_monitor_unittest.cc",
"task/cancelable_task_tracker_unittest.cc", "task/cancelable_task_tracker_unittest.cc",
"task/common/checked_lock_unittest.cc", "task/common/checked_lock_unittest.cc",
"task/common/intrusive_heap_unittest.cc",
"task/common/operations_controller_unittest.cc", "task/common/operations_controller_unittest.cc",
"task/common/task_annotator_unittest.cc", "task/common/task_annotator_unittest.cc",
"task/lazy_task_runner_unittest.cc", "task/lazy_task_runner_unittest.cc",
......
// Copyright 2018 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/containers/intrusive_heap.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
namespace base {
////////////////////////////////////////////////////////////////////////////////
// HeapHandle
// static
HeapHandle HeapHandle::Invalid() {
return HeapHandle();
}
////////////////////////////////////////////////////////////////////////////////
// InternalHeapHandleStorage
InternalHeapHandleStorage::InternalHeapHandleStorage()
: handle_(new HeapHandle()) {}
InternalHeapHandleStorage::InternalHeapHandleStorage(
InternalHeapHandleStorage&& other) noexcept
: handle_(std::move(other.handle_)) {
DCHECK(intrusive_heap::IsInvalid(other.handle_));
}
InternalHeapHandleStorage::~InternalHeapHandleStorage() = default;
InternalHeapHandleStorage& InternalHeapHandleStorage::operator=(
InternalHeapHandleStorage&& other) noexcept {
handle_ = std::move(other.handle_);
DCHECK(intrusive_heap::IsInvalid(other.handle_));
return *this;
}
void InternalHeapHandleStorage::swap(
InternalHeapHandleStorage& other) noexcept {
std::swap(handle_, other.handle_);
}
} // namespace base
This diff is collapsed.
This diff is collapsed.
...@@ -5,234 +5,74 @@ ...@@ -5,234 +5,74 @@
#ifndef BASE_TASK_COMMON_INTRUSIVE_HEAP_H_ #ifndef BASE_TASK_COMMON_INTRUSIVE_HEAP_H_
#define BASE_TASK_COMMON_INTRUSIVE_HEAP_H_ #define BASE_TASK_COMMON_INTRUSIVE_HEAP_H_
#include <algorithm> #include "base/containers/intrusive_heap.h"
#include <vector>
#include "base/logging.h"
namespace base { namespace base {
namespace internal { namespace internal {
template <typename T> using HeapHandle = base::HeapHandle;
class IntrusiveHeap;
// Intended as an opaque wrapper around |index_|.
class HeapHandle {
public:
HeapHandle() : index_(0u) {}
bool IsValid() const { return index_ != 0u; }
private:
template <typename T>
friend class IntrusiveHeap;
HeapHandle(size_t index) : index_(index) {} template <typename T>
struct IntrusiveHeapImpl {
struct GreaterUsingLessEqual {
bool operator()(const T& t1, const T& t2) const { return t2 <= t1; }
};
size_t index_; using type = base::IntrusiveHeap<T, GreaterUsingLessEqual>;
}; };
// A standard min-heap with the following assumptions: // base/task wants a min-heap that uses the <= operator, whereas
// 1. T has operator <= // base::IntrusiveHeap is a max-heap by default. This is a very thin adapter
// 2. T has method void SetHeapHandle(HeapHandle handle) // over that class that exposes minimal functionality required by the
// 3. T has method void ClearHeapHandle() // base/task IntrusiveHeap clients.
// 4. T is moveable
// 5. T is default constructible
// 6. The heap size never gets terribly big so reclaiming memory on pop/erase
// isn't a priority.
//
// The reason IntrusiveHeap exists is to provide similar performance to
// std::priority_queue while allowing removal of arbitrary elements.
template <typename T> template <typename T>
class IntrusiveHeap { class IntrusiveHeap : private IntrusiveHeapImpl<T>::type {
public: public:
IntrusiveHeap() : nodes_(kMinimumHeapSize), size_(0) {} using IntrusiveHeapImplType = typename IntrusiveHeapImpl<T>::type;
~IntrusiveHeap() { // The majority of sets in the scheduler have 0-3 items in them (a few will
for (size_t i = 1; i <= size_; i++) { // have perhaps up to 100), so this means we usually only have to allocate
MakeHole(i); // memory once.
} static constexpr size_t kMinimumHeapSize = 4;
}
IntrusiveHeap& operator=(IntrusiveHeap&& other) {
nodes_ = std::move(other.nodes_);
size_ = other.size_;
other.size_ = 0; IntrusiveHeap() { IntrusiveHeapImplType::reserve(kMinimumHeapSize); }
return *this; ~IntrusiveHeap() = default;
}
bool empty() const { return size_ == 0; } IntrusiveHeap& operator=(IntrusiveHeap&& other) = default;
size_t size() const { return size_; } bool empty() const { return IntrusiveHeapImplType::empty(); }
size_t size() const { return IntrusiveHeapImplType::size(); }
void Clear() { void Clear() {
for (size_t i = 1; i <= size_; i++) { IntrusiveHeapImplType::clear();
MakeHole(i); IntrusiveHeapImplType::reserve(kMinimumHeapSize);
}
nodes_.resize(kMinimumHeapSize);
size_ = 0;
} }
const T& Min() const { const T& Min() const { return IntrusiveHeapImplType::top(); }
DCHECK_GE(size_, 1u); void Pop() { IntrusiveHeapImplType::pop(); }
return nodes_[1];
}
void Pop() {
DCHECK_GE(size_, 1u);
MakeHole(1u);
size_t top_index = size_--;
if (!empty())
MoveHoleDownAndFillWithLeafElement(1u, std::move(nodes_[top_index]));
}
void insert(T&& element) { void insert(T&& element) {
size_++; IntrusiveHeapImplType::insert(std::move(element));
if (size_ >= nodes_.size())
nodes_.resize(nodes_.size() * 2);
// Notionally we have a hole in the tree at index |size_|, move this up
// to find the right insertion point.
MoveHoleUpAndFillWithElement(size_, std::move(element));
} }
void erase(HeapHandle handle) { void erase(HeapHandle handle) { IntrusiveHeapImplType::erase(handle); }
DCHECK_GT(handle.index_, 0u);
DCHECK_LE(handle.index_, size_);
MakeHole(handle.index_);
size_t top_index = size_--;
if (empty() || top_index == handle.index_)
return;
if (nodes_[handle.index_] <= nodes_[top_index]) {
MoveHoleDownAndFillWithLeafElement(handle.index_,
std::move(nodes_[top_index]));
} else {
MoveHoleUpAndFillWithElement(handle.index_, std::move(nodes_[top_index]));
}
}
void ReplaceMin(T&& element) { void ReplaceMin(T&& element) {
// Note |element| might not be a leaf node so we can't use IntrusiveHeapImplType::ReplaceTop(std::move(element));
// MoveHoleDownAndFillWithLeafElement.
MoveHoleDownAndFillWithElement(1u, std::move(element));
} }
void ChangeKey(HeapHandle handle, T&& element) { void ChangeKey(HeapHandle handle, T&& element) {
if (nodes_[handle.index_] <= element) { IntrusiveHeapImplType::Replace(handle, std::move(element));
MoveHoleDownAndFillWithLeafElement(handle.index_, std::move(element));
} else {
MoveHoleUpAndFillWithElement(handle.index_, std::move(element));
}
} }
const T& at(HeapHandle handle) const { const T& at(HeapHandle handle) const {
DCHECK(handle.IsValid()); return IntrusiveHeapImplType::at(handle);
return nodes_[handle.index_];
}
// Caution mutating the heap invalidates the iterators.
const T* begin() const { return &nodes_[1u]; }
const T* end() const { return begin() + size_; }
private:
enum {
// The majority of sets in the scheduler have 0-3 items in them (a few will
// have perhaps up to 100), so this means we usually only have to allocate
// memory once.
kMinimumHeapSize = 4u
};
friend class IntrusiveHeapTest;
size_t MoveHole(size_t new_hole_pos, size_t old_hole_pos) {
DCHECK_GT(new_hole_pos, 0u);
DCHECK_LE(new_hole_pos, size_);
DCHECK_GT(new_hole_pos, 0u);
DCHECK_LE(new_hole_pos, size_);
DCHECK_NE(old_hole_pos, new_hole_pos);
nodes_[old_hole_pos] = std::move(nodes_[new_hole_pos]);
nodes_[old_hole_pos].SetHeapHandle(HeapHandle(old_hole_pos));
return new_hole_pos;
}
// Notionally creates a hole in the tree at |index|.
void MakeHole(size_t index) {
DCHECK_GT(index, 0u);
DCHECK_LE(index, size_);
nodes_[index].ClearHeapHandle();
}
void FillHole(size_t hole, T&& element) {
DCHECK_GT(hole, 0u);
DCHECK_LE(hole, size_);
nodes_[hole] = std::move(element);
nodes_[hole].SetHeapHandle(HeapHandle(hole));
DCHECK(std::is_heap(begin(), end(), CompareNodes));
}
// is_heap requires a strict comparator.
static bool CompareNodes(const T& a, const T& b) { return !(a <= b); }
// Moves the |hole| up the tree and when the right position has been found
// |element| is moved in.
void MoveHoleUpAndFillWithElement(size_t hole, T&& element) {
DCHECK_GT(hole, 0u);
DCHECK_LE(hole, size_);
while (hole >= 2u) {
size_t parent_pos = hole / 2;
if (nodes_[parent_pos] <= element)
break;
hole = MoveHole(parent_pos, hole);
}
FillHole(hole, std::move(element));
}
// Moves the |hole| down the tree and when the right position has been found
// |element| is moved in.
void MoveHoleDownAndFillWithElement(size_t hole, T&& element) {
DCHECK_GT(hole, 0u);
DCHECK_LE(hole, size_);
size_t child_pos = hole * 2;
while (child_pos < size_) {
if (nodes_[child_pos + 1] <= nodes_[child_pos])
child_pos++;
if (element <= nodes_[child_pos])
break;
hole = MoveHole(child_pos, hole);
child_pos *= 2;
}
if (child_pos == size_ && !(element <= nodes_[child_pos]))
hole = MoveHole(child_pos, hole);
FillHole(hole, std::move(element));
}
// Moves the |hole| down the tree and when the right position has been found
// |leaf_element| is moved in. Faster than MoveHoleDownAndFillWithElement
// (it does one key comparison per level instead of two) but only valid for
// leaf elements (i.e. one of the max values).
void MoveHoleDownAndFillWithLeafElement(size_t hole, T&& leaf_element) {
DCHECK_GT(hole, 0u);
DCHECK_LE(hole, size_);
size_t child_pos = hole * 2;
while (child_pos < size_) {
size_t second_child = child_pos + 1;
if (nodes_[second_child] <= nodes_[child_pos])
child_pos = second_child;
hole = MoveHole(child_pos, hole);
child_pos *= 2;
}
if (child_pos == size_)
hole = MoveHole(child_pos, hole);
MoveHoleUpAndFillWithElement(hole, std::move(leaf_element));
} }
std::vector<T> nodes_; // NOTE we use 1-based indexing // Caution, mutating the heap invalidates iterators!
size_t size_; const T* begin() const { return IntrusiveHeapImplType::data(); }
const T* end() const { return IntrusiveHeapImplType::data() + size(); }
}; };
} // namespace internal } // namespace internal
......
// 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/common/intrusive_heap.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace internal {
namespace {
struct TestElement {
int key;
HeapHandle* handle;
bool operator<=(const TestElement& other) const { return key <= other.key; }
void SetHeapHandle(HeapHandle h) {
if (handle)
*handle = h;
}
void ClearHeapHandle() {
if (handle)
*handle = HeapHandle();
}
};
} // namespace
class IntrusiveHeapTest : public testing::Test {
protected:
static bool CompareNodes(const TestElement& a, const TestElement& b) {
return IntrusiveHeap<TestElement>::CompareNodes(a, b);
}
};
TEST_F(IntrusiveHeapTest, Basic) {
IntrusiveHeap<TestElement> heap;
EXPECT_TRUE(heap.empty());
EXPECT_EQ(0u, heap.size());
}
TEST_F(IntrusiveHeapTest, Clear) {
IntrusiveHeap<TestElement> heap;
HeapHandle index1;
heap.insert({11, &index1});
EXPECT_EQ(1u, heap.size());
EXPECT_TRUE(index1.IsValid());
heap.Clear();
EXPECT_EQ(0u, heap.size());
EXPECT_FALSE(index1.IsValid());
}
TEST_F(IntrusiveHeapTest, Destructor) {
HeapHandle index1;
{
IntrusiveHeap<TestElement> heap;
heap.insert({11, &index1});
EXPECT_EQ(1u, heap.size());
EXPECT_TRUE(index1.IsValid());
}
EXPECT_FALSE(index1.IsValid());
}
TEST_F(IntrusiveHeapTest, Min) {
IntrusiveHeap<TestElement> heap;
heap.insert({9, nullptr});
heap.insert({10, nullptr});
heap.insert({8, nullptr});
heap.insert({2, nullptr});
heap.insert({7, nullptr});
heap.insert({15, nullptr});
heap.insert({22, nullptr});
heap.insert({3, nullptr});
EXPECT_FALSE(heap.empty());
EXPECT_EQ(8u, heap.size());
EXPECT_EQ(2, heap.Min().key);
}
TEST_F(IntrusiveHeapTest, InsertAscending) {
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 50; i++)
heap.insert({i, nullptr});
EXPECT_EQ(0, heap.Min().key);
EXPECT_EQ(50u, heap.size());
}
TEST_F(IntrusiveHeapTest, InsertDescending) {
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 50; i++)
heap.insert({50 - i, nullptr});
EXPECT_EQ(1, heap.Min().key);
EXPECT_EQ(50u, heap.size());
}
TEST_F(IntrusiveHeapTest, HeapIndex) {
HeapHandle index5;
HeapHandle index4;
HeapHandle index3;
HeapHandle index2;
HeapHandle index1;
IntrusiveHeap<TestElement> heap;
EXPECT_FALSE(index1.IsValid());
EXPECT_FALSE(index2.IsValid());
EXPECT_FALSE(index3.IsValid());
EXPECT_FALSE(index4.IsValid());
EXPECT_FALSE(index5.IsValid());
heap.insert({15, &index5});
heap.insert({14, &index4});
heap.insert({13, &index3});
heap.insert({12, &index2});
heap.insert({11, &index1});
EXPECT_TRUE(index1.IsValid());
EXPECT_TRUE(index2.IsValid());
EXPECT_TRUE(index3.IsValid());
EXPECT_TRUE(index4.IsValid());
EXPECT_TRUE(index5.IsValid());
EXPECT_FALSE(heap.empty());
}
TEST_F(IntrusiveHeapTest, Pop) {
IntrusiveHeap<TestElement> heap;
HeapHandle index1;
HeapHandle index2;
heap.insert({11, &index1});
heap.insert({12, &index2});
EXPECT_EQ(2u, heap.size());
EXPECT_TRUE(index1.IsValid());
EXPECT_TRUE(index2.IsValid());
heap.Pop();
EXPECT_EQ(1u, heap.size());
EXPECT_FALSE(index1.IsValid());
EXPECT_TRUE(index2.IsValid());
heap.Pop();
EXPECT_EQ(0u, heap.size());
EXPECT_FALSE(index1.IsValid());
EXPECT_FALSE(index2.IsValid());
}
TEST_F(IntrusiveHeapTest, PopMany) {
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 500; i++)
heap.insert({i, nullptr});
EXPECT_FALSE(heap.empty());
EXPECT_EQ(500u, heap.size());
for (int i = 0; i < 500; i++) {
EXPECT_EQ(i, heap.Min().key);
heap.Pop();
}
EXPECT_TRUE(heap.empty());
}
TEST_F(IntrusiveHeapTest, Erase) {
IntrusiveHeap<TestElement> heap;
HeapHandle index12;
heap.insert({15, nullptr});
heap.insert({14, nullptr});
heap.insert({13, nullptr});
heap.insert({12, &index12});
heap.insert({11, nullptr});
EXPECT_EQ(5u, heap.size());
EXPECT_TRUE(index12.IsValid());
heap.erase(index12);
EXPECT_EQ(4u, heap.size());
EXPECT_FALSE(index12.IsValid());
EXPECT_EQ(11, heap.Min().key);
heap.Pop();
EXPECT_EQ(13, heap.Min().key);
heap.Pop();
EXPECT_EQ(14, heap.Min().key);
heap.Pop();
EXPECT_EQ(15, heap.Min().key);
heap.Pop();
EXPECT_TRUE(heap.empty());
}
TEST_F(IntrusiveHeapTest, ReplaceMin) {
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 500; i++)
heap.insert({500 - i, nullptr});
EXPECT_EQ(1, heap.Min().key);
for (int i = 0; i < 500; i++)
heap.ReplaceMin({1000 + i, nullptr});
EXPECT_EQ(1000, heap.Min().key);
}
TEST_F(IntrusiveHeapTest, ReplaceMinWithNonLeafNode) {
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 50; i++) {
heap.insert({i, nullptr});
heap.insert({200 + i, nullptr});
}
EXPECT_EQ(0, heap.Min().key);
for (int i = 0; i < 50; i++)
heap.ReplaceMin({100 + i, nullptr});
for (int i = 0; i < 50; i++) {
EXPECT_EQ((100 + i), heap.Min().key);
heap.Pop();
}
for (int i = 0; i < 50; i++) {
EXPECT_EQ((200 + i), heap.Min().key);
heap.Pop();
}
EXPECT_TRUE(heap.empty());
}
TEST_F(IntrusiveHeapTest, ReplaceMinCheckAllFinalPositions) {
HeapHandle index[100];
for (int j = -1; j <= 201; j += 2) {
IntrusiveHeap<TestElement> heap;
for (size_t i = 0; i < 100; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ReplaceMin({j, &index[40]});
int prev = -2;
while (!heap.empty()) {
DCHECK_GT(heap.Min().key, prev);
DCHECK(heap.Min().key == j || (heap.Min().key % 2) == 0);
DCHECK_NE(heap.Min().key, 0);
prev = heap.Min().key;
heap.Pop();
}
}
}
TEST_F(IntrusiveHeapTest, ChangeKeyUp) {
IntrusiveHeap<TestElement> heap;
HeapHandle index[10];
for (size_t i = 0; i < 10; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ChangeKey(index[5], {17, &index[5]});
std::vector<int> results;
while (!heap.empty()) {
results.push_back(heap.Min().key);
heap.Pop();
}
EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
}
TEST_F(IntrusiveHeapTest, ChangeKeyUpButDoesntMove) {
IntrusiveHeap<TestElement> heap;
HeapHandle index[10];
for (size_t i = 0; i < 10; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ChangeKey(index[5], {11, &index[5]});
std::vector<int> results;
while (!heap.empty()) {
results.push_back(heap.Min().key);
heap.Pop();
}
EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
}
TEST_F(IntrusiveHeapTest, ChangeKeyDown) {
IntrusiveHeap<TestElement> heap;
HeapHandle index[10];
for (size_t i = 0; i < 10; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ChangeKey(index[5], {1, &index[5]});
std::vector<int> results;
while (!heap.empty()) {
results.push_back(heap.Min().key);
heap.Pop();
}
EXPECT_THAT(results, testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
}
TEST_F(IntrusiveHeapTest, ChangeKeyDownButDoesntMove) {
IntrusiveHeap<TestElement> heap;
HeapHandle index[10];
for (size_t i = 0; i < 10; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ChangeKey(index[5], {9, &index[5]});
std::vector<int> results;
while (!heap.empty()) {
results.push_back(heap.Min().key);
heap.Pop();
}
EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
}
TEST_F(IntrusiveHeapTest, ChangeKeyCheckAllFinalPositions) {
HeapHandle index[100];
for (int j = -1; j <= 201; j += 2) {
IntrusiveHeap<TestElement> heap;
for (size_t i = 0; i < 100; i++) {
heap.insert({static_cast<int>(i) * 2, &index[i]});
}
heap.ChangeKey(index[40], {j, &index[40]});
int prev = -2;
while (!heap.empty()) {
DCHECK_GT(heap.Min().key, prev);
DCHECK(heap.Min().key == j || (heap.Min().key % 2) == 0);
DCHECK_NE(heap.Min().key, 80);
prev = heap.Min().key;
heap.Pop();
}
}
}
TEST_F(IntrusiveHeapTest, CompareNodes) {
TestElement five{5, nullptr}, six{6, nullptr};
// Check that we have a strict comparator, otherwise std::is_heap()
// (used in DCHECK) may fail. See http://crbug.com/661080.
EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(six, six));
EXPECT_FALSE(IntrusiveHeapTest::CompareNodes(five, six));
EXPECT_TRUE(IntrusiveHeapTest::CompareNodes(six, five));
}
TEST_F(IntrusiveHeapTest, At) {
HeapHandle index[10];
IntrusiveHeap<TestElement> heap;
for (int i = 0; i < 10; i++)
heap.insert({static_cast<int>(i ^ (i + 1)), &index[i]});
for (int i = 0; i < 10; i++) {
EXPECT_EQ(heap.at(index[i]).key, i ^ (i + 1));
EXPECT_EQ(heap.at(index[i]).handle, &index[i]);
}
}
} // namespace internal
} // namespace base
...@@ -142,6 +142,8 @@ class BASE_EXPORT TimeDomain { ...@@ -142,6 +142,8 @@ class BASE_EXPORT TimeDomain {
DCHECK(queue->heap_handle().IsValid()); DCHECK(queue->heap_handle().IsValid());
queue->set_heap_handle(base::internal::HeapHandle()); queue->set_heap_handle(base::internal::HeapHandle());
} }
HeapHandle GetHeapHandle() const { return queue->heap_handle(); }
}; };
internal::SequenceManagerImpl* sequence_manager_; // Not owned. internal::SequenceManagerImpl* sequence_manager_; // Not owned.
......
...@@ -111,6 +111,8 @@ class BASE_EXPORT WorkQueueSets { ...@@ -111,6 +111,8 @@ class BASE_EXPORT WorkQueueSets {
void ClearHeapHandle() { void ClearHeapHandle() {
value->set_heap_handle(base::internal::HeapHandle()); value->set_heap_handle(base::internal::HeapHandle());
} }
HeapHandle GetHeapHandle() const { return value->heap_handle(); }
}; };
const char* const name_; const char* const name_;
......
...@@ -92,6 +92,9 @@ class BASE_EXPORT DelayedTaskManager { ...@@ -92,6 +92,9 @@ class BASE_EXPORT DelayedTaskManager {
// Required by IntrusiveHeap. // Required by IntrusiveHeap.
void ClearHeapHandle() {} void ClearHeapHandle() {}
// Required by IntrusiveHeap.
HeapHandle GetHeapHandle() const { return HeapHandle::Invalid(); }
private: private:
bool scheduled_ = false; bool scheduled_ = false;
DISALLOW_COPY_AND_ASSIGN(DelayedTask); DISALLOW_COPY_AND_ASSIGN(DelayedTask);
......
...@@ -59,9 +59,15 @@ class PriorityQueue::TaskSourceAndSortKey { ...@@ -59,9 +59,15 @@ class PriorityQueue::TaskSourceAndSortKey {
void ClearHeapHandle() { void ClearHeapHandle() {
// Ensure |task_source_| is not nullptr, which may be the case if // Ensure |task_source_| is not nullptr, which may be the case if
// take_task_source() was called before this. // take_task_source() was called before this.
if (task_source_) { if (task_source_)
task_source_->ClearHeapHandle(); task_source_->ClearHeapHandle();
} }
// Required by IntrusiveHeap.
HeapHandle GetHeapHandle() const {
if (task_source_)
return task_source_->GetHeapHandle();
return HeapHandle::Invalid();
} }
const TaskSource* task_source() const { return task_source_.get(); } const TaskSource* task_source() const { return task_source_.get(); }
......
...@@ -201,6 +201,8 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> { ...@@ -201,6 +201,8 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
// Support for IntrusiveHeap. // Support for IntrusiveHeap.
void SetHeapHandle(const HeapHandle& handle); void SetHeapHandle(const HeapHandle& handle);
void ClearHeapHandle(); void ClearHeapHandle();
HeapHandle GetHeapHandle() const { return heap_handle_; }
HeapHandle heap_handle() const { return heap_handle_; } HeapHandle heap_handle() const { return heap_handle_; }
// Returns the shutdown behavior of all Tasks in the TaskSource. Can be // Returns the shutdown behavior of all Tasks in the TaskSource. Can be
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment