1 // Copyright 2022 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/delayed_priority_queue.h"
6
7 #include <utility>
8
9 #include "base/check_op.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/stl_util.h"
12
13 namespace base::internal {
14
15 // A class combining a TaskSource and the delayed_sort_key that determines its
16 // position in a DelayedPriorityQueue. Instances are only mutable via
17 // take_task_source() which can only be called once and renders its instance
18 // invalid after the call.
19 class DelayedPriorityQueue::TaskSourceAndDelayedSortKey {
20 public:
21 TaskSourceAndDelayedSortKey() = default;
TaskSourceAndDelayedSortKey(scoped_refptr<TaskSource> task_source,const TimeTicks & delayed_sort_key)22 TaskSourceAndDelayedSortKey(scoped_refptr<TaskSource> task_source,
23 const TimeTicks& delayed_sort_key)
24 : task_source_(std::move(task_source)),
25 delayed_sort_key_(delayed_sort_key) {
26 DCHECK(task_source_);
27 }
28 TaskSourceAndDelayedSortKey(const TaskSourceAndDelayedSortKey&) = delete;
29 TaskSourceAndDelayedSortKey& operator=(const TaskSourceAndDelayedSortKey&) =
30 delete;
31
32 // Note: while |task_source_| should always be non-null post-move (i.e. we
33 // shouldn't be moving an invalid TaskSourceAndDelayedSortKey around), there
34 // can't be a DCHECK(task_source_) on moves as IntrusiveHeap moves elements on
35 // pop instead of overwriting them: resulting in the move of a
36 // TaskSourceAndDelayedSortKey with a null |task_source_| in
37 // Transaction::Pop()'s implementation.
38 TaskSourceAndDelayedSortKey(TaskSourceAndDelayedSortKey&& other) = default;
39 TaskSourceAndDelayedSortKey& operator=(TaskSourceAndDelayedSortKey&& other) =
40 default;
41
42 // Extracts |task_source_| from this object. This object is invalid after this
43 // call.
take_task_source()44 scoped_refptr<TaskSource> take_task_source() {
45 DCHECK(task_source_);
46 task_source_->ClearDelayedHeapHandle();
47 return std::move(task_source_);
48 }
49
50 // Compares this TaskSourceAndDelayedSortKey to |other| based on their
51 // respective |delayed_sort_key_|. Used for a max-heap.
operator <(const TaskSourceAndDelayedSortKey & other) const52 bool operator<(const TaskSourceAndDelayedSortKey& other) const {
53 return delayed_sort_key_ < other.delayed_sort_key_;
54 }
55
56 // Required by IntrusiveHeap.
SetHeapHandle(const HeapHandle & handle)57 void SetHeapHandle(const HeapHandle& handle) {
58 DCHECK(task_source_);
59 task_source_->SetDelayedHeapHandle(handle);
60 }
61
62 // Required by IntrusiveHeap.
ClearHeapHandle()63 void ClearHeapHandle() {
64 // Ensure |task_source_| is not nullptr, which may be the case if
65 // take_task_source() was called before this.
66 if (task_source_)
67 task_source_->ClearDelayedHeapHandle();
68 }
69
70 // Required by IntrusiveHeap.
GetHeapHandle() const71 HeapHandle GetHeapHandle() const {
72 if (task_source_)
73 return task_source_->GetDelayedHeapHandle();
74 return HeapHandle::Invalid();
75 }
76
task_source() const77 scoped_refptr<TaskSource> task_source() const { return task_source_; }
78
delayed_sort_key() const79 TimeTicks delayed_sort_key() const { return delayed_sort_key_; }
80
81 private:
82 scoped_refptr<TaskSource> task_source_;
83 TimeTicks delayed_sort_key_;
84 };
85
86 DelayedPriorityQueue::DelayedPriorityQueue() = default;
87
~DelayedPriorityQueue()88 DelayedPriorityQueue::~DelayedPriorityQueue() {
89 if (!is_flush_task_sources_on_destroy_enabled_)
90 return;
91
92 while (!container_.empty()) {
93 auto task_source = PopTaskSource();
94 task_source->ClearForTesting(); // IN-TEST
95 }
96 }
97
98 DelayedPriorityQueue& DelayedPriorityQueue::operator=(
99 DelayedPriorityQueue&& other) = default;
100
Push(scoped_refptr<TaskSource> task_source,TimeTicks task_source_delayed_sort_key)101 void DelayedPriorityQueue::Push(scoped_refptr<TaskSource> task_source,
102 TimeTicks task_source_delayed_sort_key) {
103 container_.insert(TaskSourceAndDelayedSortKey(std::move(task_source),
104 task_source_delayed_sort_key));
105 }
106
PeekDelayedSortKey() const107 const TimeTicks DelayedPriorityQueue::PeekDelayedSortKey() const {
108 DCHECK(!IsEmpty());
109 return container_.top().delayed_sort_key();
110 }
111
PeekTaskSource() const112 scoped_refptr<TaskSource> DelayedPriorityQueue::PeekTaskSource() const {
113 DCHECK(!IsEmpty());
114
115 auto& task_source_and_delayed_sort_key = container_.top();
116 return task_source_and_delayed_sort_key.task_source();
117 }
118
PopTaskSource()119 scoped_refptr<TaskSource> DelayedPriorityQueue::PopTaskSource() {
120 DCHECK(!IsEmpty());
121
122 auto task_source_and_delayed_sort_key = container_.take_top();
123 scoped_refptr<TaskSource> task_source =
124 task_source_and_delayed_sort_key.take_task_source();
125 return task_source;
126 }
127
RemoveTaskSource(scoped_refptr<TaskSource> task_source)128 scoped_refptr<TaskSource> DelayedPriorityQueue::RemoveTaskSource(
129 scoped_refptr<TaskSource> task_source) {
130 if (IsEmpty())
131 return nullptr;
132
133 const HeapHandle heap_handle = task_source->delayed_heap_handle();
134 if (!heap_handle.IsValid())
135 return nullptr;
136
137 TaskSourceAndDelayedSortKey& task_source_and_delayed_sort_key =
138 const_cast<DelayedPriorityQueue::TaskSourceAndDelayedSortKey&>(
139 container_.at(heap_handle));
140 DCHECK_EQ(task_source_and_delayed_sort_key.task_source(), task_source);
141 task_source = task_source_and_delayed_sort_key.take_task_source();
142
143 container_.erase(heap_handle);
144 return task_source;
145 }
146
UpdateDelayedSortKey(scoped_refptr<TaskSource> task_source)147 void DelayedPriorityQueue::UpdateDelayedSortKey(
148 scoped_refptr<TaskSource> task_source) {
149 if (IsEmpty())
150 return;
151
152 const HeapHandle heap_handle = task_source->delayed_heap_handle();
153 if (!heap_handle.IsValid())
154 return;
155
156 DCHECK_EQ(container_.at(heap_handle).task_source(), task_source);
157
158 task_source = const_cast<DelayedPriorityQueue::TaskSourceAndDelayedSortKey&>(
159 container_.at(heap_handle))
160 .take_task_source();
161 auto delayed_sort_key = task_source->GetDelayedSortKey();
162 container_.Replace(
163 heap_handle,
164 TaskSourceAndDelayedSortKey(std::move(task_source), delayed_sort_key));
165 }
166
IsEmpty() const167 bool DelayedPriorityQueue::IsEmpty() const {
168 return container_.empty();
169 }
170
Size() const171 size_t DelayedPriorityQueue::Size() const {
172 return container_.size();
173 }
174
EnableFlushTaskSourcesOnDestroyForTesting()175 void DelayedPriorityQueue::EnableFlushTaskSourcesOnDestroyForTesting() {
176 DCHECK(!is_flush_task_sources_on_destroy_enabled_);
177 is_flush_task_sources_on_destroy_enabled_ = true;
178 }
179
180 // Delayed tasks are ordered by latest_delayed_run_time(). The top task may
181 // not be the first task eligible to run, but tasks will always become ripe
182 // before their latest_delayed_run_time().
operator ()(const TaskSourceAndDelayedSortKey & lhs,const TaskSourceAndDelayedSortKey & rhs) const183 bool DelayedPriorityQueue::DelayedPriorityQueueComparisonOperator::operator()(
184 const TaskSourceAndDelayedSortKey& lhs,
185 const TaskSourceAndDelayedSortKey& rhs) const {
186 return lhs.delayed_sort_key() > rhs.delayed_sort_key();
187 }
188
189 } // namespace base::internal
190