xref: /aosp_15_r20/external/grpc-grpc/test/core/iomgr/timer_heap_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/lib/iomgr/timer_heap.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include <gtest/gtest.h>
25 
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 
29 #include "src/core/lib/gpr/useful.h"
30 #include "src/core/lib/gprpp/crash.h"
31 #include "src/core/lib/iomgr/port.h"
32 #include "test/core/util/test_config.h"
33 
random_deadline(void)34 static gpr_atm random_deadline(void) { return rand(); }
35 
create_test_elements(size_t num_elements)36 static grpc_timer* create_test_elements(size_t num_elements) {
37   grpc_timer* elems =
38       static_cast<grpc_timer*>(gpr_malloc(num_elements * sizeof(grpc_timer)));
39   size_t i;
40   for (i = 0; i < num_elements; i++) {
41     elems[i].deadline = random_deadline();
42   }
43   return elems;
44 }
45 
contains(grpc_timer_heap * pq,grpc_timer * el)46 static int contains(grpc_timer_heap* pq, grpc_timer* el) {
47   size_t i;
48   for (i = 0; i < pq->timer_count; i++) {
49     if (pq->timers[i] == el) return 1;
50   }
51   return 0;
52 }
53 
check_valid(grpc_timer_heap * pq)54 static void check_valid(grpc_timer_heap* pq) {
55   size_t i;
56   for (i = 0; i < pq->timer_count; ++i) {
57     size_t left_child = 1u + 2u * i;
58     size_t right_child = left_child + 1u;
59     if (left_child < pq->timer_count) {
60       ASSERT_LE(pq->timers[i]->deadline, pq->timers[left_child]->deadline);
61     }
62     if (right_child < pq->timer_count) {
63       ASSERT_LE(pq->timers[i]->deadline, pq->timers[right_child]->deadline);
64     }
65   }
66 }
67 
68 //******************************************************************************
69 // test1
70 //
71 
test1(void)72 static void test1(void) {
73   grpc_timer_heap pq;
74   const size_t num_test_elements = 200;
75   const size_t num_test_operations = 10000;
76   size_t i;
77   grpc_timer* test_elements = create_test_elements(num_test_elements);
78   uint8_t* inpq = static_cast<uint8_t*>(gpr_malloc(num_test_elements));
79 
80   gpr_log(GPR_INFO, "test1");
81 
82   grpc_timer_heap_init(&pq);
83   memset(inpq, 0, num_test_elements);
84   ASSERT_TRUE(grpc_timer_heap_is_empty(&pq));
85   check_valid(&pq);
86   for (i = 0; i < num_test_elements; ++i) {
87     ASSERT_FALSE(contains(&pq, &test_elements[i]));
88     grpc_timer_heap_add(&pq, &test_elements[i]);
89     check_valid(&pq);
90     ASSERT_TRUE(contains(&pq, &test_elements[i]));
91     inpq[i] = 1;
92   }
93   for (i = 0; i < num_test_elements; ++i) {
94     // Test that check still succeeds even for element that wasn't just
95     // inserted.
96     ASSERT_TRUE(contains(&pq, &test_elements[i]));
97   }
98 
99   ASSERT_EQ(pq.timer_count, num_test_elements);
100 
101   check_valid(&pq);
102 
103   for (i = 0; i < num_test_operations; ++i) {
104     size_t elem_num = static_cast<size_t>(rand()) % num_test_elements;
105     grpc_timer* el = &test_elements[elem_num];
106     if (!inpq[elem_num]) {  // not in pq
107       ASSERT_FALSE(contains(&pq, el));
108       el->deadline = random_deadline();
109       grpc_timer_heap_add(&pq, el);
110       ASSERT_TRUE(contains(&pq, el));
111       inpq[elem_num] = 1;
112       check_valid(&pq);
113     } else {
114       ASSERT_TRUE(contains(&pq, el));
115       grpc_timer_heap_remove(&pq, el);
116       ASSERT_FALSE(contains(&pq, el));
117       inpq[elem_num] = 0;
118       check_valid(&pq);
119     }
120   }
121 
122   grpc_timer_heap_destroy(&pq);
123   gpr_free(test_elements);
124   gpr_free(inpq);
125 }
126 
127 //******************************************************************************
128 // test2
129 //
130 
131 typedef struct {
132   grpc_timer elem;
133   bool inserted;
134 } elem_struct;
135 
search_elems(elem_struct * elems,size_t count,bool inserted)136 static elem_struct* search_elems(elem_struct* elems, size_t count,
137                                  bool inserted) {
138   size_t* search_order =
139       static_cast<size_t*>(gpr_malloc(count * sizeof(*search_order)));
140   for (size_t i = 0; i < count; i++) {
141     search_order[i] = i;
142   }
143   for (size_t i = 0; i < count * 2; i++) {
144     size_t a = static_cast<size_t>(rand()) % count;
145     size_t b = static_cast<size_t>(rand()) % count;
146     std::swap(search_order[a], search_order[b]);
147   }
148   elem_struct* out = nullptr;
149   for (size_t i = 0; out == nullptr && i < count; i++) {
150     if (elems[search_order[i]].inserted == inserted) {
151       out = &elems[search_order[i]];
152     }
153   }
154   gpr_free(search_order);
155   return out;
156 }
157 
test2(void)158 static void test2(void) {
159   gpr_log(GPR_INFO, "test2");
160 
161   grpc_timer_heap pq;
162 
163   static const size_t elems_size = 1000;
164   elem_struct* elems =
165       static_cast<elem_struct*>(gpr_malloc(elems_size * sizeof(elem_struct)));
166   size_t num_inserted = 0;
167 
168   grpc_timer_heap_init(&pq);
169   memset(elems, 0, elems_size * sizeof(elems[0]));
170 
171   for (size_t round = 0; round < 10000; round++) {
172     int r = rand() % 1000;
173     if (r <= 550) {
174       // 55% of the time we try to add something
175       elem_struct* el = search_elems(elems, elems_size, false);
176       if (el != nullptr) {
177         el->elem.deadline = random_deadline();
178         grpc_timer_heap_add(&pq, &el->elem);
179         el->inserted = true;
180         num_inserted++;
181         check_valid(&pq);
182       }
183     } else if (r <= 650) {
184       // 10% of the time we try to remove something
185       elem_struct* el = search_elems(elems, elems_size, true);
186       if (el != nullptr) {
187         grpc_timer_heap_remove(&pq, &el->elem);
188         el->inserted = false;
189         num_inserted--;
190         check_valid(&pq);
191       }
192     } else {
193       // the remaining times we pop
194       if (num_inserted > 0) {
195         grpc_timer* top = grpc_timer_heap_top(&pq);
196         grpc_timer_heap_pop(&pq);
197         for (size_t i = 0; i < elems_size; i++) {
198           if (top == &elems[i].elem) {
199             ASSERT_TRUE(elems[i].inserted);
200             elems[i].inserted = false;
201           }
202         }
203         num_inserted--;
204         check_valid(&pq);
205       }
206     }
207 
208     if (num_inserted) {
209       int64_t* min_deadline = nullptr;
210       for (size_t i = 0; i < elems_size; i++) {
211         if (elems[i].inserted) {
212           if (min_deadline == nullptr) {
213             min_deadline = &elems[i].elem.deadline;
214           } else {
215             if (elems[i].elem.deadline < *min_deadline) {
216               min_deadline = &elems[i].elem.deadline;
217             }
218           }
219         }
220       }
221       ASSERT_EQ(grpc_timer_heap_top(&pq)->deadline, *min_deadline);
222     }
223   }
224 
225   grpc_timer_heap_destroy(&pq);
226   gpr_free(elems);
227 }
228 
shrink_test(void)229 static void shrink_test(void) {
230   gpr_log(GPR_INFO, "shrink_test");
231 
232   grpc_timer_heap pq;
233   size_t i;
234   size_t expected_size;
235 
236   // A large random number to allow for multiple shrinkages, at least 512.
237   const size_t num_elements = static_cast<size_t>(rand()) % 2000 + 512;
238 
239   grpc_timer_heap_init(&pq);
240 
241   // Create a priority queue with many elements.  Make sure the Size() is
242   // correct.
243   for (i = 0; i < num_elements; ++i) {
244     ASSERT_EQ(i, pq.timer_count);
245     grpc_timer_heap_add(&pq, create_test_elements(1));
246   }
247   ASSERT_EQ(num_elements, pq.timer_count);
248 
249   // Remove elements until the Size is 1/4 the original size.
250   while (pq.timer_count > num_elements / 4) {
251     grpc_timer* const te = pq.timers[pq.timer_count - 1];
252     grpc_timer_heap_remove(&pq, te);
253     gpr_free(te);
254   }
255   ASSERT_EQ(num_elements / 4, pq.timer_count);
256 
257   // Expect that Capacity is in the right range:
258   // Size * 2 <= Capacity <= Size * 4
259   ASSERT_LE(pq.timer_count * 2, pq.timer_capacity);
260   ASSERT_LE(pq.timer_capacity, pq.timer_count * 4);
261   check_valid(&pq);
262 
263   // Remove the rest of the elements.  Check that the Capacity is not more than
264   // 4 times the Size and not less than 2 times, but never goes below 16.
265   expected_size = pq.timer_count;
266   while (pq.timer_count > 0) {
267     const size_t which = static_cast<size_t>(rand()) % pq.timer_count;
268     grpc_timer* te = pq.timers[which];
269     grpc_timer_heap_remove(&pq, te);
270     gpr_free(te);
271     expected_size--;
272     ASSERT_EQ(expected_size, pq.timer_count);
273     ASSERT_LE(pq.timer_count * 2, pq.timer_capacity);
274     if (pq.timer_count >= 8) {
275       ASSERT_LE(pq.timer_capacity, pq.timer_count * 4);
276     } else {
277       ASSERT_LE(16, pq.timer_capacity);
278     }
279     check_valid(&pq);
280   }
281 
282   ASSERT_EQ(pq.timer_count, 0);
283   ASSERT_GE(pq.timer_capacity, 16);
284   ASSERT_LT(pq.timer_capacity, 32);
285 
286   grpc_timer_heap_destroy(&pq);
287 }
288 
TEST(TimerHeapTest,MainTest)289 TEST(TimerHeapTest, MainTest) {
290   for (int i = 0; i < 5; i++) {
291     test1();
292     test2();
293     shrink_test();
294   }
295 }
296 
main(int argc,char ** argv)297 int main(int argc, char** argv) {
298   grpc::testing::TestEnvironment env(&argc, argv);
299   ::testing::InitGoogleTest(&argc, argv);
300   return RUN_ALL_TESTS();
301 }
302