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