mirror of https://github.com/grpc/grpc.git
This change implements a platform independent alarm manager in alarm.c. It's integrated with iomgr, and some tests are cleaned up. The alarm implementation itself is a fairly direct port of LazyAlarmList from eventmanager. SpinLock has been replaced for now with gpr_mu, and other atomic operations have been dropped (again, for now). A majority of tests have been ported. Change on 2014/12/19 by ctiller <ctiller@google.com> ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=82551363pull/1/merge
parent
1a809c0ebb
commit
3bf466fb6c
16 changed files with 1214 additions and 122 deletions
@ -0,0 +1,353 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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/alarm.h" |
||||
|
||||
#include "src/core/iomgr/alarm_heap.h" |
||||
#include "src/core/iomgr/alarm_internal.h" |
||||
#include "src/core/iomgr/time_averaged_stats.h" |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
#define INVALID_HEAP_INDEX 0xffffffffu |
||||
|
||||
#define LOG2_NUM_SHARDS 5 |
||||
#define NUM_SHARDS (1 << LOG2_NUM_SHARDS) |
||||
#define MAX_ALARMS_PER_CHECK 128 |
||||
#define ADD_DEADLINE_SCALE 0.33 |
||||
#define MIN_QUEUE_WINDOW_DURATION 0.01 |
||||
#define MAX_QUEUE_WINDOW_DURATION 1 |
||||
|
||||
typedef struct { |
||||
gpr_mu mu; |
||||
grpc_time_averaged_stats stats; |
||||
/* All and only alarms with deadlines <= this will be in the heap. */ |
||||
gpr_timespec queue_deadline_cap; |
||||
gpr_timespec min_deadline; |
||||
/* Index in the g_shard_queue */ |
||||
gpr_uint32 shard_queue_index; |
||||
/* This holds all alarms with deadlines < queue_deadline_cap. Alarms in this
|
||||
list have the top bit of their deadline set to 0. */ |
||||
grpc_alarm_heap heap; |
||||
/* This holds alarms whose deadline is >= queue_deadline_cap. */ |
||||
grpc_alarm list; |
||||
} shard_type; |
||||
|
||||
/* Protects g_shard_queue */ |
||||
static gpr_mu g_mu; |
||||
/* Allow only one run_some_expired_alarms at once */ |
||||
static gpr_mu g_checker_mu; |
||||
static shard_type g_shards[NUM_SHARDS]; |
||||
/* Protected by g_mu */ |
||||
static shard_type *g_shard_queue[NUM_SHARDS]; |
||||
|
||||
static int run_some_expired_alarms(gpr_timespec now, |
||||
grpc_iomgr_cb_status status); |
||||
|
||||
static gpr_timespec compute_min_deadline(shard_type *shard) { |
||||
return grpc_alarm_heap_is_empty(&shard->heap) |
||||
? shard->queue_deadline_cap |
||||
: grpc_alarm_heap_top(&shard->heap)->deadline; |
||||
} |
||||
|
||||
void grpc_alarm_list_init(gpr_timespec now) { |
||||
int i; |
||||
|
||||
gpr_mu_init(&g_mu); |
||||
gpr_mu_init(&g_checker_mu); |
||||
|
||||
for (i = 0; i < NUM_SHARDS; i++) { |
||||
shard_type *shard = &g_shards[i]; |
||||
gpr_mu_init(&shard->mu); |
||||
grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1, |
||||
0.5); |
||||
shard->queue_deadline_cap = now; |
||||
shard->shard_queue_index = i; |
||||
grpc_alarm_heap_init(&shard->heap); |
||||
shard->list.next = shard->list.prev = &shard->list; |
||||
shard->min_deadline = compute_min_deadline(shard); |
||||
g_shard_queue[i] = shard; |
||||
} |
||||
} |
||||
|
||||
void grpc_alarm_list_shutdown() { |
||||
int i; |
||||
while (run_some_expired_alarms(gpr_inf_future, GRPC_CALLBACK_CANCELLED)) |
||||
; |
||||
for (i = 0; i < NUM_SHARDS; i++) { |
||||
shard_type *shard = &g_shards[i]; |
||||
gpr_mu_destroy(&shard->mu); |
||||
grpc_alarm_heap_destroy(&shard->heap); |
||||
} |
||||
gpr_mu_destroy(&g_mu); |
||||
gpr_mu_destroy(&g_checker_mu); |
||||
} |
||||
|
||||
/* This is a cheap, but good enough, pointer hash for sharding the tasks: */ |
||||
static size_t shard_idx(const grpc_alarm *info) { |
||||
size_t x = (size_t)info; |
||||
return ((x >> 4) ^ (x >> 9) ^ (x >> 14)) & (NUM_SHARDS - 1); |
||||
} |
||||
|
||||
static double ts_to_dbl(gpr_timespec ts) { |
||||
return ts.tv_sec + 1e-9 * ts.tv_nsec; |
||||
} |
||||
|
||||
static gpr_timespec dbl_to_ts(double d) { |
||||
gpr_timespec ts; |
||||
ts.tv_sec = d; |
||||
ts.tv_nsec = 1e9 * (d - ts.tv_sec); |
||||
return ts; |
||||
} |
||||
|
||||
static void list_join(grpc_alarm *head, grpc_alarm *alarm) { |
||||
alarm->next = head; |
||||
alarm->prev = head->prev; |
||||
alarm->next->prev = alarm->prev->next = alarm; |
||||
} |
||||
|
||||
static void list_remove(grpc_alarm *alarm) { |
||||
alarm->next->prev = alarm->prev; |
||||
alarm->prev->next = alarm->next; |
||||
} |
||||
|
||||
static void swap_adjacent_shards_in_queue(size_t first_shard_queue_index) { |
||||
shard_type *temp; |
||||
temp = g_shard_queue[first_shard_queue_index]; |
||||
g_shard_queue[first_shard_queue_index] = |
||||
g_shard_queue[first_shard_queue_index + 1]; |
||||
g_shard_queue[first_shard_queue_index + 1] = temp; |
||||
g_shard_queue[first_shard_queue_index]->shard_queue_index = |
||||
first_shard_queue_index; |
||||
g_shard_queue[first_shard_queue_index + 1]->shard_queue_index = |
||||
first_shard_queue_index + 1; |
||||
} |
||||
|
||||
static void note_deadline_change(shard_type *shard) { |
||||
while (shard->shard_queue_index > 0 && |
||||
gpr_time_cmp( |
||||
shard->min_deadline, |
||||
g_shard_queue[shard->shard_queue_index - 1]->min_deadline) < 0) { |
||||
swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); |
||||
} |
||||
while (shard->shard_queue_index < NUM_SHARDS - 1 && |
||||
gpr_time_cmp( |
||||
shard->min_deadline, |
||||
g_shard_queue[shard->shard_queue_index + 1]->min_deadline) > 0) { |
||||
swap_adjacent_shards_in_queue(shard->shard_queue_index); |
||||
} |
||||
} |
||||
|
||||
void grpc_alarm_init(grpc_alarm *alarm, gpr_timespec deadline, |
||||
grpc_iomgr_cb_func alarm_cb, void *alarm_cb_arg, |
||||
gpr_timespec now) { |
||||
int is_first_alarm = 0; |
||||
shard_type *shard = &g_shards[shard_idx(alarm)]; |
||||
alarm->cb = alarm_cb; |
||||
alarm->cb_arg = alarm_cb_arg; |
||||
alarm->deadline = deadline; |
||||
alarm->triggered = 0; |
||||
|
||||
/* TODO(ctiller): check deadline expired */ |
||||
|
||||
gpr_mu_lock(&shard->mu); |
||||
grpc_time_averaged_stats_add_sample(&shard->stats, |
||||
ts_to_dbl(gpr_time_sub(deadline, now))); |
||||
if (gpr_time_cmp(deadline, shard->queue_deadline_cap) < 0) { |
||||
is_first_alarm = grpc_alarm_heap_add(&shard->heap, alarm); |
||||
} else { |
||||
alarm->heap_index = INVALID_HEAP_INDEX; |
||||
list_join(&shard->list, alarm); |
||||
} |
||||
gpr_mu_unlock(&shard->mu); |
||||
|
||||
/* Deadline may have decreased, we need to adjust the master queue. Note
|
||||
that there is a potential racy unlocked region here. There could be a |
||||
reordering of multiple grpc_alarm_init calls, at this point, but the < test |
||||
below should ensure that we err on the side of caution. There could |
||||
also be a race with grpc_alarm_check, which might beat us to the lock. In |
||||
that case, it is possible that the alarm that we added will have already |
||||
run by the time we hold the lock, but that too is a safe error. |
||||
Finally, it's possible that the grpc_alarm_check that intervened failed to |
||||
trigger the new alarm because the min_deadline hadn't yet been reduced. |
||||
In that case, the alarm will simply have to wait for the next |
||||
grpc_alarm_check. */ |
||||
if (is_first_alarm) { |
||||
gpr_mu_lock(&g_mu); |
||||
if (gpr_time_cmp(deadline, shard->min_deadline) < 0) { |
||||
gpr_timespec old_min_deadline = g_shard_queue[0]->min_deadline; |
||||
shard->min_deadline = deadline; |
||||
note_deadline_change(shard); |
||||
if (shard->shard_queue_index == 0 && |
||||
gpr_time_cmp(deadline, old_min_deadline) < 0) { |
||||
grpc_kick_poller(); |
||||
} |
||||
} |
||||
gpr_mu_unlock(&g_mu); |
||||
} |
||||
} |
||||
|
||||
void grpc_alarm_cancel(grpc_alarm *alarm) { |
||||
shard_type *shard = &g_shards[shard_idx(alarm)]; |
||||
int triggered = 0; |
||||
gpr_mu_lock(&shard->mu); |
||||
if (!alarm->triggered) { |
||||
triggered = 1; |
||||
alarm->triggered = 1; |
||||
if (alarm->heap_index == INVALID_HEAP_INDEX) { |
||||
list_remove(alarm); |
||||
} else { |
||||
grpc_alarm_heap_remove(&shard->heap, alarm); |
||||
} |
||||
} |
||||
gpr_mu_unlock(&shard->mu); |
||||
|
||||
if (triggered) { |
||||
alarm->cb(alarm->cb_arg, GRPC_CALLBACK_CANCELLED); |
||||
} |
||||
} |
||||
|
||||
/* This is called when the queue is empty and "now" has reached the
|
||||
queue_deadline_cap. We compute a new queue deadline and then scan the map |
||||
for alarms that fall at or under it. Returns true if the queue is no |
||||
longer empty. |
||||
REQUIRES: shard->mu locked */ |
||||
static int refill_queue(shard_type *shard, gpr_timespec now) { |
||||
/* Compute the new queue window width and bound by the limits: */ |
||||
double computed_deadline_delta = |
||||
grpc_time_averaged_stats_update_average(&shard->stats) * |
||||
ADD_DEADLINE_SCALE; |
||||
double deadline_delta = |
||||
GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION, |
||||
MAX_QUEUE_WINDOW_DURATION); |
||||
grpc_alarm *alarm, *next; |
||||
|
||||
/* Compute the new cap and put all alarms under it into the queue: */ |
||||
shard->queue_deadline_cap = gpr_time_add( |
||||
gpr_time_max(now, shard->queue_deadline_cap), dbl_to_ts(deadline_delta)); |
||||
for (alarm = shard->list.next; alarm != &shard->list; alarm = next) { |
||||
next = alarm->next; |
||||
|
||||
if (gpr_time_cmp(alarm->deadline, shard->queue_deadline_cap) < 0) { |
||||
list_remove(alarm); |
||||
grpc_alarm_heap_add(&shard->heap, alarm); |
||||
} |
||||
} |
||||
return !grpc_alarm_heap_is_empty(&shard->heap); |
||||
} |
||||
|
||||
/* This pops the next non-cancelled alarm with deadline <= now from the queue,
|
||||
or returns NULL if there isn't one. |
||||
REQUIRES: shard->mu locked */ |
||||
static grpc_alarm *pop_one(shard_type *shard, gpr_timespec now) { |
||||
grpc_alarm *alarm; |
||||
for (;;) { |
||||
if (grpc_alarm_heap_is_empty(&shard->heap)) { |
||||
if (gpr_time_cmp(now, shard->queue_deadline_cap) < 0) return NULL; |
||||
if (!refill_queue(shard, now)) return NULL; |
||||
} |
||||
alarm = grpc_alarm_heap_top(&shard->heap); |
||||
if (gpr_time_cmp(alarm->deadline, now) > 0) return NULL; |
||||
alarm->triggered = 1; |
||||
grpc_alarm_heap_pop(&shard->heap); |
||||
return alarm; |
||||
} |
||||
} |
||||
|
||||
/* REQUIRES: shard->mu unlocked */ |
||||
static size_t pop_alarms(shard_type *shard, gpr_timespec now, |
||||
grpc_alarm **alarms, size_t max_alarms, |
||||
gpr_timespec *new_min_deadline) { |
||||
size_t n = 0; |
||||
grpc_alarm *alarm; |
||||
gpr_mu_lock(&shard->mu); |
||||
while (n < max_alarms && (alarm = pop_one(shard, now))) { |
||||
alarms[n++] = alarm; |
||||
} |
||||
*new_min_deadline = compute_min_deadline(shard); |
||||
gpr_mu_unlock(&shard->mu); |
||||
return n; |
||||
} |
||||
|
||||
static int run_some_expired_alarms(gpr_timespec now, |
||||
grpc_iomgr_cb_status status) { |
||||
size_t n = 0; |
||||
size_t i; |
||||
grpc_alarm *alarms[MAX_ALARMS_PER_CHECK]; |
||||
|
||||
/* TODO(ctiller): verify that there are any alarms (atomically) here */ |
||||
|
||||
if (gpr_mu_trylock(&g_checker_mu)) { |
||||
gpr_mu_lock(&g_mu); |
||||
|
||||
while (n < MAX_ALARMS_PER_CHECK && |
||||
gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { |
||||
gpr_timespec new_min_deadline; |
||||
|
||||
/* For efficiency, we pop as many available alarms as we can from the
|
||||
shard. This may violate perfect alarm deadline ordering, but that |
||||
shouldn't be a big deal because we don't make ordering guarantees. */ |
||||
n += pop_alarms(g_shard_queue[0], now, alarms + n, |
||||
MAX_ALARMS_PER_CHECK - n, &new_min_deadline); |
||||
|
||||
/* An grpc_alarm_init() on the shard could intervene here, adding a new
|
||||
alarm that is earlier than new_min_deadline. However, |
||||
grpc_alarm_init() will block on the master_lock before it can call |
||||
set_min_deadline, so this one will complete first and then the AddAlarm |
||||
will reduce the min_deadline (perhaps unnecessarily). */ |
||||
g_shard_queue[0]->min_deadline = new_min_deadline; |
||||
note_deadline_change(g_shard_queue[0]); |
||||
} |
||||
|
||||
gpr_mu_unlock(&g_mu); |
||||
gpr_mu_unlock(&g_checker_mu); |
||||
} |
||||
|
||||
for (i = 0; i < n; i++) { |
||||
alarms[i]->cb(alarms[i]->cb_arg, status); |
||||
} |
||||
|
||||
return n; |
||||
} |
||||
|
||||
int grpc_alarm_check(gpr_timespec now) { |
||||
return run_some_expired_alarms(now, GRPC_CALLBACK_SUCCESS); |
||||
} |
||||
|
||||
gpr_timespec grpc_alarm_list_next_timeout() { |
||||
gpr_timespec out; |
||||
gpr_mu_lock(&g_mu); |
||||
out = g_shard_queue[0]->min_deadline; |
||||
gpr_mu_unlock(&g_mu); |
||||
return out; |
||||
} |
@ -0,0 +1,148 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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/alarm_heap.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
/* Adjusts a heap so as to move a hole at position i closer to the root,
|
||||
until a suitable position is found for element t. Then, copies t into that |
||||
position. This functor is called each time immediately after modifying a |
||||
value in the underlying container, with the offset of the modified element as |
||||
its argument. */ |
||||
static void adjust_upwards(grpc_alarm **first, int i, grpc_alarm *t) { |
||||
while (i > 0) { |
||||
int parent = (i - 1) / 2; |
||||
if (gpr_time_cmp(first[parent]->deadline, t->deadline) >= 0) break; |
||||
first[i] = first[parent]; |
||||
first[i]->heap_index = i; |
||||
i = parent; |
||||
} |
||||
first[i] = t; |
||||
t->heap_index = i; |
||||
} |
||||
|
||||
/* Adjusts a heap so as to move a hole at position i farther away from the root,
|
||||
until a suitable position is found for element t. Then, copies t into that |
||||
position. */ |
||||
static void adjust_downwards(grpc_alarm **first, int i, int length, |
||||
grpc_alarm *t) { |
||||
for (;;) { |
||||
int left_child = 1 + 2 * i; |
||||
int right_child; |
||||
int next_i; |
||||
if (left_child >= length) break; |
||||
right_child = left_child + 1; |
||||
next_i = right_child < length && |
||||
gpr_time_cmp(first[left_child]->deadline, |
||||
first[right_child]->deadline) < 0 |
||||
? right_child |
||||
: left_child; |
||||
if (gpr_time_cmp(t->deadline, first[next_i]->deadline) >= 0) break; |
||||
first[i] = first[next_i]; |
||||
first[i]->heap_index = i; |
||||
i = next_i; |
||||
} |
||||
first[i] = t; |
||||
t->heap_index = i; |
||||
} |
||||
|
||||
#define SHRINK_MIN_ELEMS 8 |
||||
#define SHRINK_FULLNESS_FACTOR 2 |
||||
|
||||
static void maybe_shrink(grpc_alarm_heap *heap) { |
||||
if (heap->alarm_count >= 8 && |
||||
heap->alarm_count <= heap->alarm_capacity / SHRINK_FULLNESS_FACTOR / 2) { |
||||
heap->alarm_capacity = heap->alarm_count * SHRINK_FULLNESS_FACTOR; |
||||
heap->alarms = |
||||
gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); |
||||
} |
||||
} |
||||
|
||||
static void note_changed_priority(grpc_alarm_heap *heap, grpc_alarm *alarm) { |
||||
int i = alarm->heap_index; |
||||
int parent = (i - 1) / 2; |
||||
if (gpr_time_cmp(heap->alarms[parent]->deadline, alarm->deadline) < 0) { |
||||
adjust_upwards(heap->alarms, i, alarm); |
||||
} else { |
||||
adjust_downwards(heap->alarms, i, heap->alarm_count, alarm); |
||||
} |
||||
} |
||||
|
||||
void grpc_alarm_heap_init(grpc_alarm_heap *heap) { |
||||
memset(heap, 0, sizeof(*heap)); |
||||
} |
||||
|
||||
void grpc_alarm_heap_destroy(grpc_alarm_heap *heap) { gpr_free(heap->alarms); } |
||||
|
||||
int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm) { |
||||
if (heap->alarm_count == heap->alarm_capacity) { |
||||
heap->alarm_capacity = |
||||
GPR_MAX(heap->alarm_capacity + 1, heap->alarm_capacity * 3 / 2); |
||||
heap->alarms = |
||||
gpr_realloc(heap->alarms, heap->alarm_capacity * sizeof(grpc_alarm *)); |
||||
} |
||||
alarm->heap_index = heap->alarm_count; |
||||
adjust_upwards(heap->alarms, heap->alarm_count, alarm); |
||||
heap->alarm_count++; |
||||
return alarm->heap_index == 0; |
||||
} |
||||
|
||||
void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm) { |
||||
int i = alarm->heap_index; |
||||
if (i == heap->alarm_count - 1) { |
||||
heap->alarm_count--; |
||||
maybe_shrink(heap); |
||||
return; |
||||
} |
||||
heap->alarms[i] = heap->alarms[heap->alarm_count - 1]; |
||||
heap->alarms[i]->heap_index = i; |
||||
heap->alarm_count--; |
||||
maybe_shrink(heap); |
||||
note_changed_priority(heap, heap->alarms[i]); |
||||
} |
||||
|
||||
int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap) { |
||||
return heap->alarm_count == 0; |
||||
} |
||||
|
||||
grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap) { |
||||
return heap->alarms[0]; |
||||
} |
||||
|
||||
void grpc_alarm_heap_pop(grpc_alarm_heap *heap) { |
||||
grpc_alarm_heap_remove(heap, grpc_alarm_heap_top(heap)); |
||||
} |
@ -0,0 +1,57 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_INTERNAL_IOMGR_ALARM_HEAP_H_ |
||||
#define __GRPC_INTERNAL_IOMGR_ALARM_HEAP_H_ |
||||
|
||||
#include "src/core/iomgr/alarm.h" |
||||
|
||||
typedef struct { |
||||
grpc_alarm **alarms; |
||||
int alarm_count; |
||||
int alarm_capacity; |
||||
} grpc_alarm_heap; |
||||
|
||||
/* return 1 if the new alarm is the first alarm in the heap */ |
||||
int grpc_alarm_heap_add(grpc_alarm_heap *heap, grpc_alarm *alarm); |
||||
|
||||
void grpc_alarm_heap_init(grpc_alarm_heap *heap); |
||||
void grpc_alarm_heap_destroy(grpc_alarm_heap *heap); |
||||
|
||||
void grpc_alarm_heap_remove(grpc_alarm_heap *heap, grpc_alarm *alarm); |
||||
grpc_alarm *grpc_alarm_heap_top(grpc_alarm_heap *heap); |
||||
void grpc_alarm_heap_pop(grpc_alarm_heap *heap); |
||||
|
||||
int grpc_alarm_heap_is_empty(grpc_alarm_heap *heap); |
||||
|
||||
#endif /* __GRPC_INTERNAL_IOMGR_ALARM_HEAP_H_ */ |
@ -0,0 +1,50 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef __GRPC_INTERNAL_IOMGR_ALARM_INTERNAL_H_ |
||||
#define __GRPC_INTERNAL_IOMGR_ALARM_INTERNAL_H_ |
||||
|
||||
/* iomgr internal api for dealing with alarms */ |
||||
|
||||
int grpc_alarm_check(gpr_timespec now); |
||||
|
||||
void grpc_alarm_list_init(gpr_timespec now); |
||||
void grpc_alarm_list_shutdown(); |
||||
|
||||
gpr_timespec grpc_alarm_list_next_timeout(); |
||||
|
||||
/* the following must be implemented by each iomgr implementation */ |
||||
|
||||
void grpc_kick_poller(); |
||||
|
||||
#endif /* __GRPC_INTERNAL_IOMGR_ALARM_INTERNAL_H_ */ |
@ -0,0 +1,277 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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/alarm_heap.h" |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
static gpr_timespec random_deadline() { |
||||
gpr_timespec ts; |
||||
ts.tv_sec = rand(); |
||||
ts.tv_nsec = rand(); |
||||
return ts; |
||||
} |
||||
|
||||
static grpc_alarm *create_test_elements(int num_elements) { |
||||
grpc_alarm *elems = gpr_malloc(num_elements * sizeof(grpc_alarm)); |
||||
int i; |
||||
for (i = 0; i < num_elements; i++) { |
||||
elems[i].deadline = random_deadline(); |
||||
} |
||||
return elems; |
||||
} |
||||
|
||||
static int cmp_elem(const void *a, const void *b) { |
||||
int i = *(const int *)a; |
||||
int j = *(const int *)b; |
||||
return i - j; |
||||
} |
||||
|
||||
static int *all_top(grpc_alarm_heap *pq, int *n) { |
||||
int *vec = NULL; |
||||
int *need_to_check_children; |
||||
int num_need_to_check_children = 0; |
||||
|
||||
*n = 0; |
||||
if (pq->alarm_count == 0) return vec; |
||||
need_to_check_children = gpr_malloc(pq->alarm_count * sizeof(int)); |
||||
need_to_check_children[num_need_to_check_children++] = 0; |
||||
vec = gpr_malloc(pq->alarm_count * sizeof(int)); |
||||
while (num_need_to_check_children > 0) { |
||||
int ind = need_to_check_children[0]; |
||||
int leftchild, rightchild; |
||||
num_need_to_check_children--; |
||||
memmove(need_to_check_children, need_to_check_children + 1, |
||||
num_need_to_check_children * sizeof(int)); |
||||
vec[(*n)++] = ind; |
||||
leftchild = 1 + 2 * ind; |
||||
if (leftchild < pq->alarm_count) { |
||||
if (gpr_time_cmp(pq->alarms[leftchild]->deadline, |
||||
pq->alarms[ind]->deadline) >= 0) { |
||||
need_to_check_children[num_need_to_check_children++] = leftchild; |
||||
} |
||||
rightchild = leftchild + 1; |
||||
if (rightchild < pq->alarm_count && |
||||
gpr_time_cmp(pq->alarms[rightchild]->deadline, |
||||
pq->alarms[ind]->deadline) >= 0) { |
||||
need_to_check_children[num_need_to_check_children++] = rightchild; |
||||
} |
||||
} |
||||
} |
||||
|
||||
gpr_free(need_to_check_children); |
||||
|
||||
return vec; |
||||
} |
||||
|
||||
static void check_pq_top(grpc_alarm *elements, grpc_alarm_heap *pq, |
||||
gpr_uint8 *inpq, int num_elements) { |
||||
gpr_timespec max_deadline = gpr_inf_past; |
||||
int *max_deadline_indices = gpr_malloc(num_elements * sizeof(int)); |
||||
int *top_elements; |
||||
int num_max_deadline_indices = 0; |
||||
int num_top_elements; |
||||
int i; |
||||
for (i = 0; i < num_elements; ++i) { |
||||
if (inpq[i] && gpr_time_cmp(elements[i].deadline, max_deadline) >= 0) { |
||||
if (gpr_time_cmp(elements[i].deadline, max_deadline) > 0) { |
||||
num_max_deadline_indices = 0; |
||||
max_deadline = elements[i].deadline; |
||||
} |
||||
max_deadline_indices[num_max_deadline_indices++] = elements[i].heap_index; |
||||
} |
||||
} |
||||
qsort(max_deadline_indices, num_max_deadline_indices, sizeof(int), cmp_elem); |
||||
top_elements = all_top(pq, &num_top_elements); |
||||
GPR_ASSERT(num_top_elements == num_max_deadline_indices); |
||||
for (i = 0; i < num_top_elements; i++) { |
||||
GPR_ASSERT(max_deadline_indices[i] == top_elements[i]); |
||||
} |
||||
gpr_free(max_deadline_indices); |
||||
gpr_free(top_elements); |
||||
} |
||||
|
||||
static int contains(grpc_alarm_heap *pq, grpc_alarm *el) { |
||||
int i; |
||||
for (i = 0; i < pq->alarm_count; i++) { |
||||
if (pq->alarms[i] == el) return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void check_valid(grpc_alarm_heap *pq) { |
||||
int i; |
||||
for (i = 0; i < pq->alarm_count; ++i) { |
||||
int left_child = 1 + 2 * i; |
||||
int right_child = left_child + 1; |
||||
if (left_child < pq->alarm_count) { |
||||
GPR_ASSERT(gpr_time_cmp(pq->alarms[i]->deadline, |
||||
pq->alarms[left_child]->deadline) >= 0); |
||||
} |
||||
if (right_child < pq->alarm_count) { |
||||
GPR_ASSERT(gpr_time_cmp(pq->alarms[i]->deadline, |
||||
pq->alarms[right_child]->deadline) >= 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void test1() { |
||||
grpc_alarm_heap pq; |
||||
const int num_test_elements = 200; |
||||
const int num_test_operations = 10000; |
||||
int i; |
||||
grpc_alarm *test_elements = create_test_elements(num_test_elements); |
||||
gpr_uint8 *inpq = gpr_malloc(num_test_elements); |
||||
|
||||
grpc_alarm_heap_init(&pq); |
||||
memset(inpq, 0, num_test_elements); |
||||
GPR_ASSERT(grpc_alarm_heap_is_empty(&pq)); |
||||
check_valid(&pq); |
||||
for (i = 0; i < num_test_elements; ++i) { |
||||
GPR_ASSERT(!contains(&pq, &test_elements[i])); |
||||
grpc_alarm_heap_add(&pq, &test_elements[i]); |
||||
check_valid(&pq); |
||||
GPR_ASSERT(contains(&pq, &test_elements[i])); |
||||
inpq[i] = 1; |
||||
check_pq_top(test_elements, &pq, inpq, num_test_elements); |
||||
} |
||||
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.alarm_count == num_test_elements); |
||||
|
||||
check_pq_top(test_elements, &pq, inpq, num_test_elements); |
||||
|
||||
for (i = 0; i < num_test_operations; ++i) { |
||||
int elem_num = rand() % num_test_elements; |
||||
grpc_alarm *el = &test_elements[elem_num]; |
||||
if (!inpq[elem_num]) { /* not in pq */ |
||||
GPR_ASSERT(!contains(&pq, el)); |
||||
el->deadline = random_deadline(); |
||||
grpc_alarm_heap_add(&pq, el); |
||||
GPR_ASSERT(contains(&pq, el)); |
||||
inpq[elem_num] = 1; |
||||
check_pq_top(test_elements, &pq, inpq, num_test_elements); |
||||
check_valid(&pq); |
||||
} else { |
||||
GPR_ASSERT(contains(&pq, el)); |
||||
grpc_alarm_heap_remove(&pq, el); |
||||
GPR_ASSERT(!contains(&pq, el)); |
||||
inpq[elem_num] = 0; |
||||
check_pq_top(test_elements, &pq, inpq, num_test_elements); |
||||
check_valid(&pq); |
||||
} |
||||
} |
||||
|
||||
grpc_alarm_heap_destroy(&pq); |
||||
gpr_free(test_elements); |
||||
gpr_free(inpq); |
||||
} |
||||
|
||||
static void shrink_test() { |
||||
grpc_alarm_heap pq; |
||||
int i; |
||||
int expected_size; |
||||
|
||||
/* A large random number to allow for multiple shrinkages, at least 512. */ |
||||
const int num_elements = rand() % 2000 + 512; |
||||
|
||||
grpc_alarm_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.alarm_count); |
||||
grpc_alarm_heap_add(&pq, create_test_elements(1)); |
||||
} |
||||
GPR_ASSERT(num_elements == pq.alarm_count); |
||||
|
||||
/* Remove elements until the Size is 1/4 the original size. */ |
||||
while (pq.alarm_count > num_elements / 4) { |
||||
grpc_alarm *const te = pq.alarms[pq.alarm_count - 1]; |
||||
grpc_alarm_heap_remove(&pq, te); |
||||
gpr_free(te); |
||||
} |
||||
GPR_ASSERT(num_elements / 4 == pq.alarm_count); |
||||
|
||||
/* Expect that Capacity is in the right range:
|
||||
Size * 2 <= Capacity <= Size * 4 */ |
||||
GPR_ASSERT(pq.alarm_count * 2 <= pq.alarm_capacity); |
||||
GPR_ASSERT(pq.alarm_capacity <= pq.alarm_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.alarm_count; |
||||
while (pq.alarm_count > 0) { |
||||
const int which = rand() % pq.alarm_count; |
||||
grpc_alarm *te = pq.alarms[which]; |
||||
grpc_alarm_heap_remove(&pq, te); |
||||
gpr_free(te); |
||||
expected_size--; |
||||
GPR_ASSERT(expected_size == pq.alarm_count); |
||||
GPR_ASSERT(pq.alarm_count * 2 <= pq.alarm_capacity); |
||||
if (pq.alarm_count >= 8) { |
||||
GPR_ASSERT(pq.alarm_capacity <= pq.alarm_count * 4); |
||||
} else { |
||||
GPR_ASSERT(16 <= pq.alarm_capacity); |
||||
} |
||||
check_valid(&pq); |
||||
} |
||||
|
||||
GPR_ASSERT(0 == pq.alarm_count); |
||||
GPR_ASSERT(pq.alarm_capacity >= 16 && pq.alarm_capacity < 32); |
||||
|
||||
grpc_alarm_heap_destroy(&pq); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
int i; |
||||
|
||||
grpc_test_init(argc, argv); |
||||
|
||||
for (i = 0; i < 5; i++) { |
||||
test1(); |
||||
shrink_test(); |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,144 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2014, 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/alarm.h" |
||||
|
||||
#include <string.h> |
||||
|
||||
#include "src/core/iomgr/alarm_internal.h" |
||||
#include <grpc/support/log.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
#define MAX_CB 30 |
||||
|
||||
static int cb_called[MAX_CB][GRPC_CALLBACK_DO_NOT_USE]; |
||||
static int kicks; |
||||
|
||||
void grpc_kick_poller() { ++kicks; } |
||||
|
||||
static void cb(void *arg, grpc_iomgr_cb_status status) { |
||||
cb_called[(gpr_intptr)arg][status]++; |
||||
} |
||||
|
||||
static void add_test() { |
||||
gpr_timespec start = gpr_now(); |
||||
int i; |
||||
grpc_alarm alarms[20]; |
||||
|
||||
grpc_alarm_list_init(start); |
||||
memset(cb_called, 0, sizeof(cb_called)); |
||||
|
||||
/* 10 ms alarms. will expire in the current epoch */ |
||||
for (i = 0; i < 10; i++) { |
||||
grpc_alarm_init(&alarms[i], gpr_time_add(start, gpr_time_from_millis(10)), |
||||
cb, (void *)(gpr_intptr)i, start); |
||||
} |
||||
|
||||
/* 1010 ms alarms. will expire in the next epoch */ |
||||
for (i = 10; i < 20; i++) { |
||||
grpc_alarm_init(&alarms[i], gpr_time_add(start, gpr_time_from_millis(1010)), |
||||
cb, (void *)(gpr_intptr)i, start); |
||||
} |
||||
|
||||
/* collect alarms. Only the first batch should be ready. */ |
||||
GPR_ASSERT(10 == |
||||
grpc_alarm_check(gpr_time_add(start, gpr_time_from_millis(500)))); |
||||
for (i = 0; i < 20; i++) { |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_SUCCESS] == (i < 10)); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_CANCELLED] == 0); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_TIMED_OUT] == 0); |
||||
} |
||||
|
||||
GPR_ASSERT(0 == |
||||
grpc_alarm_check(gpr_time_add(start, gpr_time_from_millis(600)))); |
||||
for (i = 0; i < 30; i++) { |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_SUCCESS] == (i < 10)); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_CANCELLED] == 0); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_TIMED_OUT] == 0); |
||||
} |
||||
|
||||
/* collect the rest of the alarms */ |
||||
GPR_ASSERT(10 == |
||||
grpc_alarm_check(gpr_time_add(start, gpr_time_from_millis(1500)))); |
||||
for (i = 0; i < 30; i++) { |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_SUCCESS] == (i < 20)); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_CANCELLED] == 0); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_TIMED_OUT] == 0); |
||||
} |
||||
|
||||
GPR_ASSERT(0 == |
||||
grpc_alarm_check(gpr_time_add(start, gpr_time_from_millis(1600)))); |
||||
for (i = 0; i < 30; i++) { |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_SUCCESS] == (i < 20)); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_CANCELLED] == 0); |
||||
GPR_ASSERT(cb_called[i][GRPC_CALLBACK_TIMED_OUT] == 0); |
||||
} |
||||
|
||||
grpc_alarm_list_shutdown(); |
||||
} |
||||
|
||||
/* Cleaning up a list with pending alarms. */ |
||||
void destruction_test() { |
||||
grpc_alarm alarms[5]; |
||||
|
||||
grpc_alarm_list_init(gpr_time_0); |
||||
memset(cb_called, 0, sizeof(cb_called)); |
||||
|
||||
grpc_alarm_init(&alarms[0], gpr_time_from_millis(100), cb, |
||||
(void *)(gpr_intptr)0, gpr_time_0); |
||||
grpc_alarm_init(&alarms[1], gpr_time_from_millis(3), cb, |
||||
(void *)(gpr_intptr)1, gpr_time_0); |
||||
grpc_alarm_init(&alarms[2], gpr_time_from_millis(100), cb, |
||||
(void *)(gpr_intptr)2, gpr_time_0); |
||||
grpc_alarm_init(&alarms[3], gpr_time_from_millis(3), cb, |
||||
(void *)(gpr_intptr)3, gpr_time_0); |
||||
grpc_alarm_init(&alarms[4], gpr_time_from_millis(1), cb, |
||||
(void *)(gpr_intptr)4, gpr_time_0); |
||||
GPR_ASSERT(1 == grpc_alarm_check(gpr_time_from_millis(2))); |
||||
GPR_ASSERT(1 == cb_called[4][GRPC_CALLBACK_SUCCESS]); |
||||
grpc_alarm_cancel(&alarms[0]); |
||||
grpc_alarm_cancel(&alarms[3]); |
||||
GPR_ASSERT(1 == cb_called[0][GRPC_CALLBACK_CANCELLED]); |
||||
GPR_ASSERT(1 == cb_called[3][GRPC_CALLBACK_CANCELLED]); |
||||
|
||||
grpc_alarm_list_shutdown(); |
||||
GPR_ASSERT(1 == cb_called[1][GRPC_CALLBACK_CANCELLED]); |
||||
GPR_ASSERT(1 == cb_called[2][GRPC_CALLBACK_CANCELLED]); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
grpc_test_init(argc, argv); |
||||
add_test(); |
||||
destruction_test(); |
||||
return 0; |
||||
} |
Loading…
Reference in new issue