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 if (task) {
95 std::move(task->task).Run();
96 }
97 }
98 }
99
100 PriorityQueue& PriorityQueue::operator=(PriorityQueue&& other) = default;
101
Push(RegisteredTaskSource task_source,TaskSourceSortKey task_source_sort_key)102 void PriorityQueue::Push(RegisteredTaskSource task_source,
103 TaskSourceSortKey task_source_sort_key) {
104 container_.insert(
105 TaskSourceAndSortKey(std::move(task_source), task_source_sort_key));
106 IncrementNumTaskSourcesForPriority(task_source_sort_key.priority());
107 }
108
PeekSortKey() const109 const TaskSourceSortKey& PriorityQueue::PeekSortKey() const {
110 DCHECK(!IsEmpty());
111 return container_.top().sort_key();
112 }
113
PeekTaskSource() const114 RegisteredTaskSource& PriorityQueue::PeekTaskSource() const {
115 DCHECK(!IsEmpty());
116
117 // The const_cast on Min() is okay since modifying the TaskSource cannot alter
118 // the sort order of TaskSourceAndSortKey.
119 auto& task_source_and_sort_key =
120 const_cast<PriorityQueue::TaskSourceAndSortKey&>(container_.top());
121 return task_source_and_sort_key.task_source();
122 }
123
PopTaskSource()124 RegisteredTaskSource PriorityQueue::PopTaskSource() {
125 DCHECK(!IsEmpty());
126
127 // The const_cast on Min() is okay since the TaskSourceAndSortKey is
128 // transactionally being popped from |container_| right after and taking its
129 // TaskSource does not alter its sort order.
130 auto& task_source_and_sort_key =
131 const_cast<TaskSourceAndSortKey&>(container_.top());
132 DecrementNumTaskSourcesForPriority(
133 task_source_and_sort_key.sort_key().priority());
134 RegisteredTaskSource task_source =
135 task_source_and_sort_key.take_task_source();
136 container_.pop();
137 return task_source;
138 }
139
RemoveTaskSource(const TaskSource & task_source)140 RegisteredTaskSource PriorityQueue::RemoveTaskSource(
141 const TaskSource& task_source) {
142 if (IsEmpty())
143 return nullptr;
144
145 const HeapHandle heap_handle = task_source.immediate_heap_handle();
146 if (!heap_handle.IsValid())
147 return nullptr;
148
149 TaskSourceAndSortKey& task_source_and_sort_key =
150 const_cast<PriorityQueue::TaskSourceAndSortKey&>(
151 container_.at(heap_handle));
152 DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source);
153 RegisteredTaskSource registered_task_source =
154 task_source_and_sort_key.take_task_source();
155
156 DecrementNumTaskSourcesForPriority(
157 task_source_and_sort_key.sort_key().priority());
158 container_.erase(heap_handle);
159 return registered_task_source;
160 }
161
UpdateSortKey(const TaskSource & task_source,TaskSourceSortKey sort_key)162 void PriorityQueue::UpdateSortKey(const TaskSource& task_source,
163 TaskSourceSortKey sort_key) {
164 if (IsEmpty())
165 return;
166
167 const HeapHandle heap_handle = task_source.immediate_heap_handle();
168 if (!heap_handle.IsValid())
169 return;
170
171 auto old_sort_key = container_.at(heap_handle).sort_key();
172 auto registered_task_source =
173 const_cast<PriorityQueue::TaskSourceAndSortKey&>(
174 container_.at(heap_handle))
175 .take_task_source();
176
177 DecrementNumTaskSourcesForPriority(old_sort_key.priority());
178 IncrementNumTaskSourcesForPriority(sort_key.priority());
179
180 container_.Replace(
181 heap_handle,
182 TaskSourceAndSortKey(std::move(registered_task_source), sort_key));
183 }
184
IsEmpty() const185 bool PriorityQueue::IsEmpty() const {
186 return container_.empty();
187 }
188
Size() const189 size_t PriorityQueue::Size() const {
190 return container_.size();
191 }
192
EnableFlushTaskSourcesOnDestroyForTesting()193 void PriorityQueue::EnableFlushTaskSourcesOnDestroyForTesting() {
194 DCHECK(!is_flush_task_sources_on_destroy_enabled_);
195 is_flush_task_sources_on_destroy_enabled_ = true;
196 }
197
swap(PriorityQueue & other)198 void PriorityQueue::swap(PriorityQueue& other) {
199 container_.swap(other.container_);
200 num_task_sources_per_priority_.swap(other.num_task_sources_per_priority_);
201 std::swap(is_flush_task_sources_on_destroy_enabled_,
202 other.is_flush_task_sources_on_destroy_enabled_);
203 }
204
DecrementNumTaskSourcesForPriority(TaskPriority priority)205 void PriorityQueue::DecrementNumTaskSourcesForPriority(TaskPriority priority) {
206 DCHECK_GT(num_task_sources_per_priority_[base::to_underlying(priority)], 0U);
207 --num_task_sources_per_priority_[base::to_underlying(priority)];
208 }
209
IncrementNumTaskSourcesForPriority(TaskPriority priority)210 void PriorityQueue::IncrementNumTaskSourcesForPriority(TaskPriority priority) {
211 ++num_task_sources_per_priority_[base::to_underlying(priority)];
212 }
213
214 } // namespace internal
215 } // namespace base
216