xref: /aosp_15_r20/external/cronet/base/task/thread_pool/priority_queue.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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