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