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/sequence_manager/hierarchical_timing_wheel.h"
6
7 #include <math.h>
8
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace base::sequence_manager {
13
14 namespace {
15
16 // Custom comparator for testing.
17 template <typename T>
18 struct CustomCompare {
operator ()base::sequence_manager::__anon3cb16c0b0111::CustomCompare19 bool operator()(const T& lhs, const T& rhs) const {
20 return std::tie(lhs.delayed_run_time, lhs.name) >
21 std::tie(rhs.delayed_run_time, rhs.name);
22 }
23 };
24
25 class Task {
26 public:
27 enum : size_t { kInvalidIndex = std::numeric_limits<size_t>::max() };
28
Task(TimeTicks delayed_run_time,std::string name=std::string ())29 explicit Task(TimeTicks delayed_run_time, std::string name = std::string())
30 : delayed_run_time(delayed_run_time),
31 name(name),
32 handle_(std::make_unique<HierarchicalTimingWheelHandle>()) {}
33
handle() const34 HierarchicalTimingWheelHandle* handle() const { return handle_.get(); }
35
36 TimeTicks delayed_run_time;
37
38 // Used as a second comparator key to test the custom comparator
39 // functionality.
40 std::string name;
41
42 private:
43 std::unique_ptr<HierarchicalTimingWheelHandle> handle_;
44 };
45
46 } // namespace
47
48 // Tests the construction of the object.
TEST(HierarchicalTimingWheelTest,SimpleTest)49 TEST(HierarchicalTimingWheelTest, SimpleTest) {
50 const TimeTicks baseline = TimeTicks::Now();
51 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
52 baseline};
53
54 EXPECT_EQ(hierarchical_timing_wheel.Size(), 0u);
55
56 auto* handle =
57 hierarchical_timing_wheel.Insert(Task{baseline + Microseconds(100)})
58 ->handle();
59 EXPECT_EQ(hierarchical_timing_wheel.Size(), 1u);
60
61 hierarchical_timing_wheel.Remove(*handle);
62 EXPECT_EQ(hierarchical_timing_wheel.Size(), 0u);
63 }
64
65 // Tests whether an element can be added in all the places in the hierarchy.
TEST(HierarchicalTimingWheelTest,InsertAllDistinctElements)66 TEST(HierarchicalTimingWheelTest, InsertAllDistinctElements) {
67 const size_t kHierarchyCount = 6;
68 const TimeTicks baseline = TimeTicks::Now();
69 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
70 baseline};
71
72 // Create time delta to insert a task in each of the hierarchy. The element's
73 // index represents the hierarchy index in which it will be inserted. The
74 // delays are chosen as per the example given in the class's header file.
75 const TimeDelta kDelay[kHierarchyCount] = {
76 Microseconds(100), Microseconds(500), Milliseconds(50),
77 Seconds(5), Seconds(500), Seconds(50000)};
78
79 HierarchicalTimingWheelHandle* handles[kHierarchyCount];
80 for (size_t i = 0; i < kHierarchyCount; i++) {
81 handles[i] =
82 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
83 }
84
85 for (size_t i = 0; i < kHierarchyCount; i++) {
86 EXPECT_EQ(handles[i]->GetHierarchyIndex(), i);
87
88 const bool is_heap_handle = i == 0 || i == kHierarchyCount - 1;
89 EXPECT_EQ(handles[i]->GetHeapHandle().IsValid(), is_heap_handle);
90 EXPECT_EQ(handles[i]->GetTimingWheelHandle().IsValid(), !is_heap_handle);
91 }
92 }
93
94 // Tests whether multiple elements can be added in the same place in the
95 // hierarchy.
TEST(HierarchicalTimingWheelTest,InsertSimilarElements)96 TEST(HierarchicalTimingWheelTest, InsertSimilarElements) {
97 const size_t kTotalElements = 3;
98 const size_t kExpectedHierarchyIndex = 1;
99 const TimeTicks baseline = TimeTicks::Now();
100 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
101 baseline};
102
103 // Create time delta to insert a task in the second hierarchy.
104 const TimeDelta kDelay[kTotalElements] = {Microseconds(500), Milliseconds(21),
105 Milliseconds(49)};
106
107 HierarchicalTimingWheelHandle* handles[kTotalElements];
108 for (size_t i = 0; i < kTotalElements; i++) {
109 handles[i] =
110 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
111 }
112
113 for (auto* handle : handles) {
114 EXPECT_EQ(handle->GetHierarchyIndex(), kExpectedHierarchyIndex);
115 EXPECT_EQ(handle->GetHeapHandle().IsValid(), false);
116 EXPECT_EQ(handle->GetTimingWheelHandle().IsValid(), true);
117 }
118 }
119
120 // Tests whether the hierarchy can be updated and cascading take place from one
121 // hierarchy to another for an element.
TEST(HierarchicalTimingWheelTest,UpdateOneElement)122 TEST(HierarchicalTimingWheelTest, UpdateOneElement) {
123 const size_t kHierarchyCount = 6;
124 TimeTicks baseline = TimeTicks::Now();
125 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
126 baseline};
127
128 // An array of deltas which cascades an element from the biggest
129 // hierarchy to the smallest sequentially, and then finally expiring the
130 // element.
131 const TimeDelta kTimeDelta[] = {Seconds(50000) - Seconds(500),
132 Seconds(500) - Seconds(5),
133 Seconds(5) - Milliseconds(50),
134 Milliseconds(50) - Microseconds(500),
135 Microseconds(500) - Microseconds(100),
136 Microseconds(100)};
137
138 // Create time delta to insert a task at the end of the hierarchy.
139 const TimeTicks delayed_run_time = baseline + Seconds(50000);
140
141 HierarchicalTimingWheelHandle* handle =
142 hierarchical_timing_wheel.Insert(Task{delayed_run_time})->handle();
143
144 std::vector<Task> expired_tasks;
145 for (size_t i = 0; i < kHierarchyCount; i++) {
146 const size_t expected_hierarchy_index = kHierarchyCount - i - 1;
147 EXPECT_EQ(handle->GetHierarchyIndex(), expected_hierarchy_index);
148 baseline += kTimeDelta[i];
149 expired_tasks = hierarchical_timing_wheel.Update(baseline);
150
151 // An element will be returned as expired on the last Update.
152 const bool expired = i == kHierarchyCount - 1;
153
154 // We expect one element to be returned, once.
155 EXPECT_EQ(expired_tasks.size() == 0, !expired);
156 EXPECT_EQ(expired_tasks.size() == 1, expired);
157 }
158 }
159
160 // Tests whether the hierarchy can be updated and cascading take place of
161 // multiple existing elements in the hierarchy.
TEST(HierarchicalTimingWheelTest,UpdateMultipleElements)162 TEST(HierarchicalTimingWheelTest, UpdateMultipleElements) {
163 const size_t kHierarchyCount = 6;
164 TimeTicks baseline = TimeTicks::Now();
165 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
166 baseline};
167
168 // Create time delta to insert a task in each of the hierarchy. The element's
169 // index represents the hierarchy index in which it will be inserted.
170 const TimeDelta kDelay[kHierarchyCount] = {
171 Microseconds(100), Microseconds(500), Milliseconds(50),
172 Seconds(5), Seconds(500), Seconds(50000)};
173
174 HierarchicalTimingWheelHandle* handles[kHierarchyCount];
175 for (size_t i = 0; i < kHierarchyCount; i++) {
176 handles[i] =
177 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
178 }
179
180 // This update expires all the inserted elements except the last two.
181 baseline += Seconds(499);
182 std::vector<Task> expired_tasks = hierarchical_timing_wheel.Update(baseline);
183 EXPECT_EQ(expired_tasks.size(), 4u);
184 EXPECT_EQ(handles[kHierarchyCount - 2]->GetHierarchyIndex(), 2u);
185 EXPECT_EQ(handles[kHierarchyCount - 1]->GetHierarchyIndex(), 4u);
186
187 // Expires the last two elements by updating much more than latest delay
188 // element.
189 baseline += Seconds(100000);
190 expired_tasks = hierarchical_timing_wheel.Update(baseline);
191 EXPECT_EQ(expired_tasks.size(), 2u);
192 }
193
194 // Tests whether an element can be removed from each hierarchy.
TEST(HierarchicalTimingWheelTest,RemoveElements)195 TEST(HierarchicalTimingWheelTest, RemoveElements) {
196 const size_t kHierarchyCount = 6;
197 const TimeTicks baseline = TimeTicks::Now();
198 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
199 baseline};
200
201 // Create time delta to insert a task in each of the hierarchy. The element's
202 // index represents the hierarchy index in which it will be inserted.
203 const TimeDelta kDelay[kHierarchyCount] = {
204 Microseconds(100), Microseconds(500), Milliseconds(50),
205 Seconds(5), Seconds(500), Seconds(50000)};
206
207 HierarchicalTimingWheelHandle* handles[kHierarchyCount];
208 for (size_t i = 0; i < kHierarchyCount; i++) {
209 handles[i] =
210 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
211 }
212
213 for (auto* handle : handles) {
214 hierarchical_timing_wheel.Remove(*handle);
215 }
216
217 // The biggest delay was Seconds(50000). Hence, this would remove any leftover
218 // element, which there aren't supposed to be.
219 std::vector<Task> expired_tasks =
220 hierarchical_timing_wheel.Update(baseline + Seconds(50000));
221 EXPECT_EQ(expired_tasks.empty(), true);
222 }
223
224 // Tests whether the top element of the hierarchy returned is correct when all
225 // distinct elements exist in the hierarchy.
TEST(HierarchicalTimingWheelTest,TopDifferentElements)226 TEST(HierarchicalTimingWheelTest, TopDifferentElements) {
227 const size_t kHierarchyCount = 6;
228 const TimeTicks baseline = TimeTicks::Now();
229 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
230 baseline};
231
232 // Create time delta to insert a task in each of the hierarchy. The element's
233 // index represents the hierarchy index in which it will be inserted.
234 const TimeDelta kDelay[kHierarchyCount] = {
235 Microseconds(100), Microseconds(500), Milliseconds(50),
236 Seconds(5), Seconds(500), Seconds(50000)};
237
238 HierarchicalTimingWheelHandle* handles[kHierarchyCount];
239 for (size_t i = 0; i < kHierarchyCount; i++) {
240 handles[i] =
241 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
242 const Task& task = hierarchical_timing_wheel.Top();
243 EXPECT_EQ(task.delayed_run_time, baseline + kDelay[0]);
244 }
245
246 for (size_t i = 0; i < kHierarchyCount; i++) {
247 const Task& task = hierarchical_timing_wheel.Top();
248 EXPECT_EQ(task.delayed_run_time, baseline + kDelay[i]);
249 hierarchical_timing_wheel.Remove(*handles[i]);
250 }
251 }
252
253 // Tests whether the top element of the hierarchy returned is correct when
254 // multiple similar elements are in the hierarchy.
TEST(HierarchicalTimingWheelTest,TopSimilarElements)255 TEST(HierarchicalTimingWheelTest, TopSimilarElements) {
256 const size_t kTotalElements = 3;
257 const TimeTicks baseline = TimeTicks::Now();
258 HierarchicalTimingWheel<Task, 4, 100, 500> hierarchical_timing_wheel{
259 baseline};
260
261 // Create time delta to insert a task in the first hierarchy.
262 const TimeDelta kDelay[kTotalElements] = {
263 Microseconds(100), Microseconds(200), Microseconds(300)};
264
265 HierarchicalTimingWheelHandle* handles[kTotalElements];
266 for (size_t i = 0; i < kTotalElements; i++) {
267 handles[i] =
268 hierarchical_timing_wheel.Insert(Task{baseline + kDelay[i]})->handle();
269 const Task& task = hierarchical_timing_wheel.Top();
270 EXPECT_EQ(task.delayed_run_time, baseline + kDelay[0]);
271 }
272
273 for (size_t i = 0; i < kTotalElements; i++) {
274 const Task& task = hierarchical_timing_wheel.Top();
275 EXPECT_EQ(task.delayed_run_time, baseline + kDelay[i]);
276 hierarchical_timing_wheel.Remove(*handles[i]);
277 }
278 }
279
280 // Tests whether the |Compare| functor is correctly used.
TEST(HierarchicalTimingWheelTest,CustomComparator)281 TEST(HierarchicalTimingWheelTest, CustomComparator) {
282 const std::string expectedTopTaskName = "a";
283 const TimeTicks baseline = TimeTicks::Now();
284 HierarchicalTimingWheel<Task, 4, 100, 500,
285 DefaultHierarchicalTimingWheelHandleAccessor<Task>,
286 GetDelayedRunTime<Task>, CustomCompare<Task>>
287 hierarchical_timing_wheel{baseline};
288
289 // Create time delta to insert a task in the first hierarchy.
290 const TimeDelta kDelay = Microseconds(100);
291
292 // Inserts two elements in the same bucket.
293 hierarchical_timing_wheel.Insert(Task{baseline + kDelay, "z"});
294 hierarchical_timing_wheel.Insert(Task{baseline + kDelay, "a"});
295 const Task& task = hierarchical_timing_wheel.Top();
296
297 // The custom comparator orders by the name's lexicographical order,
298 // since both the elements have the same delayed run time.
299 EXPECT_EQ(task.name, expectedTopTaskName);
300 }
301
302 } // namespace base::sequence_manager
303