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