• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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