1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task/thread_pool/priority_queue.h"
6
7 #include <utility>
8
9 #include "base/check_op.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/types/cxx23_to_underlying.h"
12
13 namespace base {
14 namespace internal {
15
16 // A class combining a TaskSource and the TaskSourceSortKey that determines its
17 // position in a PriorityQueue. Instances are only mutable via
18 // take_task_source() which can only be called once and renders its instance
19 // invalid after the call.
20 class PriorityQueue::TaskSourceAndSortKey {
21 public:
22 TaskSourceAndSortKey() = default;
TaskSourceAndSortKey(RegisteredTaskSource task_source,const TaskSourceSortKey & sort_key)23 TaskSourceAndSortKey(RegisteredTaskSource task_source,
24 const TaskSourceSortKey& sort_key)
25 : task_source_(std::move(task_source)), sort_key_(sort_key) {
26 DCHECK(task_source_);
27 }
28 TaskSourceAndSortKey(const TaskSourceAndSortKey&) = delete;
29 TaskSourceAndSortKey& operator=(const TaskSourceAndSortKey&) = delete;
30
31 // Note: while |task_source_| should always be non-null post-move (i.e. we
32 // shouldn't be moving an invalid TaskSourceAndSortKey around), there can't be
33 // a DCHECK(task_source_) on moves as IntrusiveHeap moves elements on pop
34 // instead of overwriting them: resulting in the move of a
35 // TaskSourceAndSortKey with a null |task_source_| in Transaction::Pop()'s
36 // implementation.
37 TaskSourceAndSortKey(TaskSourceAndSortKey&& other) = default;
38 TaskSourceAndSortKey& operator=(TaskSourceAndSortKey&& other) = default;
39
40 // Extracts |task_source_| from this object. This object is invalid after this
41 // call.
take_task_source()42 RegisteredTaskSource take_task_source() {
43 DCHECK(task_source_);
44 task_source_->ClearImmediateHeapHandle();
45 return std::move(task_source_);
46 }
47
48 // Compares this TaskSourceAndSortKey to |other| based on their respective
49 // |sort_key_|. Used for a max-heap.
operator <(const TaskSourceAndSortKey & other) const50 bool operator<(const TaskSourceAndSortKey& other) const {
51 return sort_key_ < other.sort_key_;
52 }
53
54 // Required by IntrusiveHeap.
SetHeapHandle(const HeapHandle & handle)55 void SetHeapHandle(const HeapHandle& handle) {
56 DCHECK(task_source_);
57 task_source_->SetImmediateHeapHandle(handle);
58 }
59
60 // Required by IntrusiveHeap.
ClearHeapHandle()61 void ClearHeapHandle() {
62 // Ensure |task_source_| is not nullptr, which may be the case if
63 // take_task_source() was called before this.
64 if (task_source_)
65 task_source_->ClearImmediateHeapHandle();
66 }
67
68 // Required by IntrusiveHeap.
GetHeapHandle() const69 HeapHandle GetHeapHandle() const {
70 if (task_source_)
71 return task_source_->GetImmediateHeapHandle();
72 return HeapHandle::Invalid();
73 }
74
task_source() const75 const RegisteredTaskSource& task_source() const { return task_source_; }
task_source()76 RegisteredTaskSource& task_source() { return task_source_; }
77
sort_key() const78 const TaskSourceSortKey& sort_key() const { return sort_key_; }
79
80 private:
81 RegisteredTaskSource task_source_;
82 TaskSourceSortKey sort_key_;
83 };
84
85 PriorityQueue::PriorityQueue() = default;
86
~PriorityQueue()87 PriorityQueue::~PriorityQueue() {
88 if (!is_flush_task_sources_on_destroy_enabled_)
89 return;
90
91 while (!container_.empty()) {
92 auto task_source = PopTaskSource();
93 auto task = task_source.Clear();
94 std::move(task.task).Run();
95 }
96 }
97
98 PriorityQueue& PriorityQueue::operator=(PriorityQueue&& other) = default;
99
Push(RegisteredTaskSource task_source,TaskSourceSortKey task_source_sort_key)100 void PriorityQueue::Push(RegisteredTaskSource task_source,
101 TaskSourceSortKey task_source_sort_key) {
102 container_.insert(
103 TaskSourceAndSortKey(std::move(task_source), task_source_sort_key));
104 IncrementNumTaskSourcesForPriority(task_source_sort_key.priority());
105 }
106
PeekSortKey() const107 const TaskSourceSortKey& PriorityQueue::PeekSortKey() const {
108 DCHECK(!IsEmpty());
109 return container_.top().sort_key();
110 }
111
PeekTaskSource() const112 RegisteredTaskSource& PriorityQueue::PeekTaskSource() const {
113 DCHECK(!IsEmpty());
114
115 // The const_cast on Min() is okay since modifying the TaskSource cannot alter
116 // the sort order of TaskSourceAndSortKey.
117 auto& task_source_and_sort_key =
118 const_cast<PriorityQueue::TaskSourceAndSortKey&>(container_.top());
119 return task_source_and_sort_key.task_source();
120 }
121
PopTaskSource()122 RegisteredTaskSource PriorityQueue::PopTaskSource() {
123 DCHECK(!IsEmpty());
124
125 // The const_cast on Min() is okay since the TaskSourceAndSortKey is
126 // transactionally being popped from |container_| right after and taking its
127 // TaskSource does not alter its sort order.
128 auto& task_source_and_sort_key =
129 const_cast<TaskSourceAndSortKey&>(container_.top());
130 DecrementNumTaskSourcesForPriority(
131 task_source_and_sort_key.sort_key().priority());
132 RegisteredTaskSource task_source =
133 task_source_and_sort_key.take_task_source();
134 container_.pop();
135 return task_source;
136 }
137
RemoveTaskSource(const TaskSource & task_source)138 RegisteredTaskSource PriorityQueue::RemoveTaskSource(
139 const TaskSource& task_source) {
140 if (IsEmpty())
141 return nullptr;
142
143 const HeapHandle heap_handle = task_source.immediate_heap_handle();
144 if (!heap_handle.IsValid())
145 return nullptr;
146
147 TaskSourceAndSortKey& task_source_and_sort_key =
148 const_cast<PriorityQueue::TaskSourceAndSortKey&>(
149 container_.at(heap_handle));
150 DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source);
151 RegisteredTaskSource registered_task_source =
152 task_source_and_sort_key.take_task_source();
153
154 DecrementNumTaskSourcesForPriority(
155 task_source_and_sort_key.sort_key().priority());
156 container_.erase(heap_handle);
157 return registered_task_source;
158 }
159
UpdateSortKey(const TaskSource & task_source,TaskSourceSortKey sort_key)160 void PriorityQueue::UpdateSortKey(const TaskSource& task_source,
161 TaskSourceSortKey sort_key) {
162 if (IsEmpty())
163 return;
164
165 const HeapHandle heap_handle = task_source.immediate_heap_handle();
166 if (!heap_handle.IsValid())
167 return;
168
169 auto old_sort_key = container_.at(heap_handle).sort_key();
170 auto registered_task_source =
171 const_cast<PriorityQueue::TaskSourceAndSortKey&>(
172 container_.at(heap_handle))
173 .take_task_source();
174
175 DecrementNumTaskSourcesForPriority(old_sort_key.priority());
176 IncrementNumTaskSourcesForPriority(sort_key.priority());
177
178 container_.Replace(
179 heap_handle,
180 TaskSourceAndSortKey(std::move(registered_task_source), sort_key));
181 }
182
IsEmpty() const183 bool PriorityQueue::IsEmpty() const {
184 return container_.empty();
185 }
186
Size() const187 size_t PriorityQueue::Size() const {
188 return container_.size();
189 }
190
EnableFlushTaskSourcesOnDestroyForTesting()191 void PriorityQueue::EnableFlushTaskSourcesOnDestroyForTesting() {
192 DCHECK(!is_flush_task_sources_on_destroy_enabled_);
193 is_flush_task_sources_on_destroy_enabled_ = true;
194 }
195
DecrementNumTaskSourcesForPriority(TaskPriority priority)196 void PriorityQueue::DecrementNumTaskSourcesForPriority(TaskPriority priority) {
197 DCHECK_GT(num_task_sources_per_priority_[base::to_underlying(priority)], 0U);
198 --num_task_sources_per_priority_[base::to_underlying(priority)];
199 }
200
IncrementNumTaskSourcesForPriority(TaskPriority priority)201 void PriorityQueue::IncrementNumTaskSourcesForPriority(TaskPriority priority) {
202 ++num_task_sources_per_priority_[base::to_underlying(priority)];
203 }
204
205 } // namespace internal
206 } // namespace base
207