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/port.h" 20 21 #include "src/core/lib/iomgr/timer_heap.h" 22 23 #include <stdlib.h> 24 #include <string.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 "test/core/util/test_config.h" 31 32 static gpr_atm random_deadline(void) { return rand(); } 33 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 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 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 GPR_ASSERT(pq->timers[i]->deadline <= pq->timers[left_child]->deadline); 59 } 60 if (right_child < pq->timer_count) { 61 GPR_ASSERT(pq->timers[i]->deadline <= pq->timers[right_child]->deadline); 62 } 63 } 64 } 65 66 /******************************************************************************* 67 * test1 68 */ 69 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 gpr_log(GPR_INFO, "test1"); 79 80 grpc_timer_heap_init(&pq); 81 memset(inpq, 0, num_test_elements); 82 GPR_ASSERT(grpc_timer_heap_is_empty(&pq)); 83 check_valid(&pq); 84 for (i = 0; i < num_test_elements; ++i) { 85 GPR_ASSERT(!contains(&pq, &test_elements[i])); 86 grpc_timer_heap_add(&pq, &test_elements[i]); 87 check_valid(&pq); 88 GPR_ASSERT(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 GPR_ASSERT(contains(&pq, &test_elements[i])); 95 } 96 97 GPR_ASSERT(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 GPR_ASSERT(!contains(&pq, el)); 106 el->deadline = random_deadline(); 107 grpc_timer_heap_add(&pq, el); 108 GPR_ASSERT(contains(&pq, el)); 109 inpq[elem_num] = 1; 110 check_valid(&pq); 111 } else { 112 GPR_ASSERT(contains(&pq, el)); 113 grpc_timer_heap_remove(&pq, el); 114 GPR_ASSERT(!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 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 GPR_SWAP(size_t, 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 156 static void test2(void) { 157 gpr_log(GPR_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); 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, GPR_ARRAY_SIZE(elems), 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, GPR_ARRAY_SIZE(elems), 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 GPR_ASSERT(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 grpc_millis* 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 GPR_ASSERT(grpc_timer_heap_top(&pq)->deadline == *min_deadline); 220 } 221 } 222 223 grpc_timer_heap_destroy(&pq); 224 gpr_free(elems); 225 } 226 227 static void shrink_test(void) { 228 gpr_log(GPR_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 GPR_ASSERT(i == pq.timer_count); 243 grpc_timer_heap_add(&pq, create_test_elements(1)); 244 } 245 GPR_ASSERT(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 GPR_ASSERT(num_elements / 4 == pq.timer_count); 254 255 /* Expect that Capacity is in the right range: 256 Size * 2 <= Capacity <= Size * 4 */ 257 GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity); 258 GPR_ASSERT(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 GPR_ASSERT(expected_size == pq.timer_count); 271 GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity); 272 if (pq.timer_count >= 8) { 273 GPR_ASSERT(pq.timer_capacity <= pq.timer_count * 4); 274 } else { 275 GPR_ASSERT(16 <= pq.timer_capacity); 276 } 277 check_valid(&pq); 278 } 279 280 GPR_ASSERT(0 == pq.timer_count); 281 GPR_ASSERT(pq.timer_capacity >= 16 && pq.timer_capacity < 32); 282 283 grpc_timer_heap_destroy(&pq); 284 } 285 286 int main(int argc, char** argv) { 287 int i; 288 289 grpc_test_init(argc, argv); 290 291 for (i = 0; i < 5; i++) { 292 test1(); 293 test2(); 294 shrink_test(); 295 } 296 297 return 0; 298 } 299