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