| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 | /* * * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * *     * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. *     * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. *     * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */#include "src/core/iomgr/timer_heap.h"#include <stdlib.h>#include <string.h>#include <grpc/support/alloc.h>#include <grpc/support/log.h>#include <grpc/support/useful.h>#include "test/core/util/test_config.h"static gpr_timespec random_deadline(void) {  gpr_timespec ts;  ts.tv_sec = rand();  ts.tv_nsec = rand();  ts.clock_type = GPR_CLOCK_REALTIME;  return ts;}static grpc_timer *create_test_elements(size_t num_elements) {  grpc_timer *elems = gpr_malloc(num_elements * sizeof(grpc_timer));  size_t i;  for (i = 0; i < num_elements; i++) {    elems[i].deadline = random_deadline();  }  return elems;}static int contains(grpc_timer_heap *pq, grpc_timer *el) {  size_t i;  for (i = 0; i < pq->timer_count; i++) {    if (pq->timers[i] == el) return 1;  }  return 0;}static void check_valid(grpc_timer_heap *pq) {  size_t i;  for (i = 0; i < pq->timer_count; ++i) {    size_t left_child = 1u + 2u * i;    size_t right_child = left_child + 1u;    if (left_child < pq->timer_count) {      GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline,                              pq->timers[left_child]->deadline) <= 0);    }    if (right_child < pq->timer_count) {      GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline,                              pq->timers[right_child]->deadline) <= 0);    }  }}/******************************************************************************* * test1 */static void test1(void) {  grpc_timer_heap pq;  const size_t num_test_elements = 200;  const size_t num_test_operations = 10000;  size_t i;  grpc_timer *test_elements = create_test_elements(num_test_elements);  uint8_t *inpq = gpr_malloc(num_test_elements);  gpr_log(GPR_INFO, "test1");  grpc_timer_heap_init(&pq);  memset(inpq, 0, num_test_elements);  GPR_ASSERT(grpc_timer_heap_is_empty(&pq));  check_valid(&pq);  for (i = 0; i < num_test_elements; ++i) {    GPR_ASSERT(!contains(&pq, &test_elements[i]));    grpc_timer_heap_add(&pq, &test_elements[i]);    check_valid(&pq);    GPR_ASSERT(contains(&pq, &test_elements[i]));    inpq[i] = 1;  }  for (i = 0; i < num_test_elements; ++i) {    /* Test that check still succeeds even for element that wasn't just       inserted. */    GPR_ASSERT(contains(&pq, &test_elements[i]));  }  GPR_ASSERT(pq.timer_count == num_test_elements);  check_valid(&pq);  for (i = 0; i < num_test_operations; ++i) {    size_t elem_num = (size_t)rand() % num_test_elements;    grpc_timer *el = &test_elements[elem_num];    if (!inpq[elem_num]) { /* not in pq */      GPR_ASSERT(!contains(&pq, el));      el->deadline = random_deadline();      grpc_timer_heap_add(&pq, el);      GPR_ASSERT(contains(&pq, el));      inpq[elem_num] = 1;      check_valid(&pq);    } else {      GPR_ASSERT(contains(&pq, el));      grpc_timer_heap_remove(&pq, el);      GPR_ASSERT(!contains(&pq, el));      inpq[elem_num] = 0;      check_valid(&pq);    }  }  grpc_timer_heap_destroy(&pq);  gpr_free(test_elements);  gpr_free(inpq);}/******************************************************************************* * test2 */typedef struct {  grpc_timer elem;  bool inserted;} elem_struct;static elem_struct *search_elems(elem_struct *elems, size_t count,                                 bool inserted) {  size_t *search_order = gpr_malloc(count * sizeof(*search_order));  for (size_t i = 0; i < count; i++) {    search_order[i] = i;  }  for (size_t i = 0; i < count * 2; i++) {    size_t a = (size_t)rand() % count;    size_t b = (size_t)rand() % count;    GPR_SWAP(size_t, search_order[a], search_order[b]);  }  elem_struct *out = NULL;  for (size_t i = 0; out == NULL && i < count; i++) {    if (elems[search_order[i]].inserted == inserted) {      out = &elems[search_order[i]];    }  }  gpr_free(search_order);  return out;}static void test2(void) {  gpr_log(GPR_INFO, "test2");  grpc_timer_heap pq;  elem_struct elems[1000];  size_t num_inserted = 0;  grpc_timer_heap_init(&pq);  memset(elems, 0, sizeof(elems));  for (size_t round = 0; round < 10000; round++) {    int r = rand() % 1000;    if (r <= 550) {      /* 55% of the time we try to add something */      elem_struct *el = search_elems(elems, GPR_ARRAY_SIZE(elems), false);      if (el != NULL) {        el->elem.deadline = random_deadline();        grpc_timer_heap_add(&pq, &el->elem);        el->inserted = true;        num_inserted++;        check_valid(&pq);      }    } else if (r <= 650) {      /* 10% of the time we try to remove something */      elem_struct *el = search_elems(elems, GPR_ARRAY_SIZE(elems), true);      if (el != NULL) {        grpc_timer_heap_remove(&pq, &el->elem);        el->inserted = false;        num_inserted--;        check_valid(&pq);      }    } else {      /* the remaining times we pop */      if (num_inserted > 0) {        grpc_timer *top = grpc_timer_heap_top(&pq);        grpc_timer_heap_pop(&pq);        for (size_t i = 0; i < GPR_ARRAY_SIZE(elems); i++) {          if (top == &elems[i].elem) {            GPR_ASSERT(elems[i].inserted);            elems[i].inserted = false;          }        }        num_inserted--;        check_valid(&pq);      }    }    if (num_inserted) {      gpr_timespec *min_deadline = NULL;      for (size_t i = 0; i < GPR_ARRAY_SIZE(elems); i++) {        if (elems[i].inserted) {          if (min_deadline == NULL) {            min_deadline = &elems[i].elem.deadline;          } else {            if (gpr_time_cmp(elems[i].elem.deadline, *min_deadline) < 0) {              min_deadline = &elems[i].elem.deadline;            }          }        }      }      GPR_ASSERT(          0 == gpr_time_cmp(grpc_timer_heap_top(&pq)->deadline, *min_deadline));    }  }  grpc_timer_heap_destroy(&pq);}static void shrink_test(void) {  gpr_log(GPR_INFO, "shrink_test");  grpc_timer_heap pq;  size_t i;  size_t expected_size;  /* A large random number to allow for multiple shrinkages, at least 512. */  const size_t num_elements = (size_t)rand() % 2000 + 512;  grpc_timer_heap_init(&pq);  /* Create a priority queue with many elements.  Make sure the Size() is     correct. */  for (i = 0; i < num_elements; ++i) {    GPR_ASSERT(i == pq.timer_count);    grpc_timer_heap_add(&pq, create_test_elements(1));  }  GPR_ASSERT(num_elements == pq.timer_count);  /* Remove elements until the Size is 1/4 the original size. */  while (pq.timer_count > num_elements / 4) {    grpc_timer *const te = pq.timers[pq.timer_count - 1];    grpc_timer_heap_remove(&pq, te);    gpr_free(te);  }  GPR_ASSERT(num_elements / 4 == pq.timer_count);  /* Expect that Capacity is in the right range:     Size * 2 <= Capacity <= Size * 4 */  GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity);  GPR_ASSERT(pq.timer_capacity <= pq.timer_count * 4);  check_valid(&pq);  /* Remove the rest of the elements.  Check that the Capacity is not more than     4 times the Size and not less than 2 times, but never goes below 16. */  expected_size = pq.timer_count;  while (pq.timer_count > 0) {    const size_t which = (size_t)rand() % pq.timer_count;    grpc_timer *te = pq.timers[which];    grpc_timer_heap_remove(&pq, te);    gpr_free(te);    expected_size--;    GPR_ASSERT(expected_size == pq.timer_count);    GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity);    if (pq.timer_count >= 8) {      GPR_ASSERT(pq.timer_capacity <= pq.timer_count * 4);    } else {      GPR_ASSERT(16 <= pq.timer_capacity);    }    check_valid(&pq);  }  GPR_ASSERT(0 == pq.timer_count);  GPR_ASSERT(pq.timer_capacity >= 16 && pq.timer_capacity < 32);  grpc_timer_heap_destroy(&pq);}int main(int argc, char **argv) {  int i;  grpc_test_init(argc, argv);  for (i = 0; i < 5; i++) {    test1();    test2();    shrink_test();  }  return 0;}
 |