From 58270d546137c3e7335e34a44bf272c4a460f40a Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 18 Mar 2016 15:57:49 -0700 Subject: [PATCH] Revert "Revert "Factor out backoff code into a separate library (to be re-used elsewhere)"" --- BUILD | 4 + Makefile | 37 ++++ binding.gyp | 1 + build.yaml | 12 +- config.m4 | 1 + gRPC.podspec | 3 + grpc.gemspec | 2 + package.json | 2 + package.xml | 2 + src/core/client_config/subchannel.c | 86 +++----- src/core/support/backoff.c | 71 +++++++ src/core/support/backoff.h | 65 ++++++ src/python/grpcio/grpc_core_dependencies.py | 1 + test/core/support/backoff_test.c | 107 ++++++++++ tools/doxygen/Doxyfile.core.internal | 2 + tools/run_tests/sources_and_headers.json | 17 ++ tools/run_tests/tests.json | 23 ++- vsprojects/buildtests_c.sln | 25 +++ vsprojects/vcxproj/gpr/gpr.vcxproj | 3 + vsprojects/vcxproj/gpr/gpr.vcxproj.filters | 6 + .../gpr_backoff_test/gpr_backoff_test.vcxproj | 193 ++++++++++++++++++ .../gpr_backoff_test.vcxproj.filters | 21 ++ 22 files changed, 619 insertions(+), 65 deletions(-) create mode 100644 src/core/support/backoff.c create mode 100644 src/core/support/backoff.h create mode 100644 test/core/support/backoff_test.c create mode 100644 vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj create mode 100644 vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj.filters diff --git a/BUILD b/BUILD index 06c3f61e1d4..659e79a1e0e 100644 --- a/BUILD +++ b/BUILD @@ -45,6 +45,7 @@ cc_library( name = "gpr", srcs = [ "src/core/profiling/timers.h", + "src/core/support/backoff.h", "src/core/support/block_annotate.h", "src/core/support/env.h", "src/core/support/load_file.h", @@ -59,6 +60,7 @@ cc_library( "src/core/profiling/stap_timers.c", "src/core/support/alloc.c", "src/core/support/avl.c", + "src/core/support/backoff.c", "src/core/support/cmdline.c", "src/core/support/cpu_iphone.c", "src/core/support/cpu_linux.c", @@ -1233,6 +1235,7 @@ objc_library( "src/core/profiling/stap_timers.c", "src/core/support/alloc.c", "src/core/support/avl.c", + "src/core/support/backoff.c", "src/core/support/cmdline.c", "src/core/support/cpu_iphone.c", "src/core/support/cpu_linux.c", @@ -1317,6 +1320,7 @@ objc_library( "include/grpc/impl/codegen/sync_win32.h", "include/grpc/impl/codegen/time.h", "src/core/profiling/timers.h", + "src/core/support/backoff.h", "src/core/support/block_annotate.h", "src/core/support/env.h", "src/core/support/load_file.h", diff --git a/Makefile b/Makefile index 0659fb1466f..44a4b12bac5 100644 --- a/Makefile +++ b/Makefile @@ -893,6 +893,7 @@ fling_test: $(BINDIR)/$(CONFIG)/fling_test gen_hpack_tables: $(BINDIR)/$(CONFIG)/gen_hpack_tables gen_legal_metadata_characters: $(BINDIR)/$(CONFIG)/gen_legal_metadata_characters gpr_avl_test: $(BINDIR)/$(CONFIG)/gpr_avl_test +gpr_backoff_test: $(BINDIR)/$(CONFIG)/gpr_backoff_test gpr_cmdline_test: $(BINDIR)/$(CONFIG)/gpr_cmdline_test gpr_cpu_test: $(BINDIR)/$(CONFIG)/gpr_cpu_test gpr_env_test: $(BINDIR)/$(CONFIG)/gpr_env_test @@ -1204,6 +1205,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/fling_stream_test \ $(BINDIR)/$(CONFIG)/fling_test \ $(BINDIR)/$(CONFIG)/gpr_avl_test \ + $(BINDIR)/$(CONFIG)/gpr_backoff_test \ $(BINDIR)/$(CONFIG)/gpr_cmdline_test \ $(BINDIR)/$(CONFIG)/gpr_cpu_test \ $(BINDIR)/$(CONFIG)/gpr_env_test \ @@ -1458,6 +1460,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/fling_test || ( echo test fling_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_avl_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_avl_test || ( echo test gpr_avl_test failed ; exit 1 ) + $(E) "[RUN] Testing gpr_backoff_test" + $(Q) $(BINDIR)/$(CONFIG)/gpr_backoff_test || ( echo test gpr_backoff_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_cmdline_test" $(Q) $(BINDIR)/$(CONFIG)/gpr_cmdline_test || ( echo test gpr_cmdline_test failed ; exit 1 ) $(E) "[RUN] Testing gpr_cpu_test" @@ -2250,6 +2254,7 @@ LIBGPR_SRC = \ src/core/profiling/stap_timers.c \ src/core/support/alloc.c \ src/core/support/avl.c \ + src/core/support/backoff.c \ src/core/support/cmdline.c \ src/core/support/cpu_iphone.c \ src/core/support/cpu_linux.c \ @@ -6619,6 +6624,38 @@ endif endif +GPR_BACKOFF_TEST_SRC = \ + test/core/support/backoff_test.c \ + +GPR_BACKOFF_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(GPR_BACKOFF_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/gpr_backoff_test: openssl_dep_error + +else + + + +$(BINDIR)/$(CONFIG)/gpr_backoff_test: $(GPR_BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(GPR_BACKOFF_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_backoff_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/support/backoff_test.o: $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + +deps_gpr_backoff_test: $(GPR_BACKOFF_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(GPR_BACKOFF_TEST_OBJS:.o=.dep) +endif +endif + + GPR_CMDLINE_TEST_SRC = \ test/core/support/cmdline_test.c \ diff --git a/binding.gyp b/binding.gyp index d1e086cfa08..bb974d6ef8a 100644 --- a/binding.gyp +++ b/binding.gyp @@ -496,6 +496,7 @@ 'src/core/profiling/stap_timers.c', 'src/core/support/alloc.c', 'src/core/support/avl.c', + 'src/core/support/backoff.c', 'src/core/support/cmdline.c', 'src/core/support/cpu_iphone.c', 'src/core/support/cpu_linux.c', diff --git a/build.yaml b/build.yaml index 01495179f51..8a655119698 100644 --- a/build.yaml +++ b/build.yaml @@ -55,6 +55,7 @@ filegroups: - include/grpc/support/useful.h headers: - src/core/profiling/timers.h + - src/core/support/backoff.h - src/core/support/block_annotate.h - src/core/support/env.h - src/core/support/load_file.h @@ -70,6 +71,7 @@ filegroups: - src/core/profiling/stap_timers.c - src/core/support/alloc.c - src/core/support/avl.c + - src/core/support/backoff.c - src/core/support/cmdline.c - src/core/support/cpu_iphone.c - src/core/support/cpu_linux.c @@ -1237,6 +1239,14 @@ targets: deps: - gpr_test_util - gpr +- name: gpr_backoff_test + build: test + language: c + src: + - test/core/support/backoff_test.c + deps: + - gpr_test_util + - gpr - name: gpr_cmdline_test build: test language: c @@ -2436,7 +2446,7 @@ targets: - linux - posix - name: qps_openloop_test - cpu_cost: 10 + cpu_cost: 0.5 build: test language: c++ src: diff --git a/config.m4 b/config.m4 index 8ab45e66037..013303838e0 100644 --- a/config.m4 +++ b/config.m4 @@ -40,6 +40,7 @@ if test "$PHP_GRPC" != "no"; then src/core/profiling/stap_timers.c \ src/core/support/alloc.c \ src/core/support/avl.c \ + src/core/support/backoff.c \ src/core/support/cmdline.c \ src/core/support/cpu_iphone.c \ src/core/support/cpu_linux.c \ diff --git a/gRPC.podspec b/gRPC.podspec index 6f26db1ac1a..8a83bd23e2b 100644 --- a/gRPC.podspec +++ b/gRPC.podspec @@ -64,6 +64,7 @@ Pod::Spec.new do |s| # Core cross-platform gRPC library, written in C. s.subspec 'C-Core' do |ss| ss.source_files = 'src/core/profiling/timers.h', + 'src/core/support/backoff.h', 'src/core/support/block_annotate.h', 'src/core/support/env.h', 'src/core/support/load_file.h', @@ -120,6 +121,7 @@ Pod::Spec.new do |s| 'src/core/profiling/stap_timers.c', 'src/core/support/alloc.c', 'src/core/support/avl.c', + 'src/core/support/backoff.c', 'src/core/support/cmdline.c', 'src/core/support/cpu_iphone.c', 'src/core/support/cpu_linux.c', @@ -470,6 +472,7 @@ Pod::Spec.new do |s| 'third_party/nanopb/pb_encode.c' ss.private_header_files = 'src/core/profiling/timers.h', + 'src/core/support/backoff.h', 'src/core/support/block_annotate.h', 'src/core/support/env.h', 'src/core/support/load_file.h', diff --git a/grpc.gemspec b/grpc.gemspec index 0bc4fcfc0e4..4480c6e5d1b 100755 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -89,6 +89,7 @@ Gem::Specification.new do |s| s.files += %w( include/grpc/impl/codegen/sync_win32.h ) s.files += %w( include/grpc/impl/codegen/time.h ) s.files += %w( src/core/profiling/timers.h ) + s.files += %w( src/core/support/backoff.h ) s.files += %w( src/core/support/block_annotate.h ) s.files += %w( src/core/support/env.h ) s.files += %w( src/core/support/load_file.h ) @@ -103,6 +104,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/profiling/stap_timers.c ) s.files += %w( src/core/support/alloc.c ) s.files += %w( src/core/support/avl.c ) + s.files += %w( src/core/support/backoff.c ) s.files += %w( src/core/support/cmdline.c ) s.files += %w( src/core/support/cpu_iphone.c ) s.files += %w( src/core/support/cpu_linux.c ) diff --git a/package.json b/package.json index e52a283d9d4..cd9668a1b0e 100644 --- a/package.json +++ b/package.json @@ -865,6 +865,7 @@ "include/grpc/impl/codegen/sync_win32.h", "include/grpc/impl/codegen/time.h", "src/core/profiling/timers.h", + "src/core/support/backoff.h", "src/core/support/block_annotate.h", "src/core/support/env.h", "src/core/support/load_file.h", @@ -879,6 +880,7 @@ "src/core/profiling/stap_timers.c", "src/core/support/alloc.c", "src/core/support/avl.c", + "src/core/support/backoff.c", "src/core/support/cmdline.c", "src/core/support/cpu_iphone.c", "src/core/support/cpu_linux.c", diff --git a/package.xml b/package.xml index da06e90e3b8..1e0bbc7e397 100644 --- a/package.xml +++ b/package.xml @@ -93,6 +93,7 @@ + @@ -107,6 +108,7 @@ + diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c index ec9e47a6ddb..5dea2156685 100644 --- a/src/core/client_config/subchannel.c +++ b/src/core/client_config/subchannel.c @@ -45,6 +45,7 @@ #include "src/core/client_config/subchannel_index.h" #include "src/core/iomgr/timer.h" #include "src/core/profiling/timers.h" +#include "src/core/support/backoff.h" #include "src/core/surface/channel.h" #include "src/core/transport/connectivity_state.h" @@ -127,8 +128,8 @@ struct grpc_subchannel { /** next connect attempt time */ gpr_timespec next_attempt; - /** amount to backoff each failure */ - gpr_timespec backoff_delta; + /** backoff state */ + gpr_backoff backoff_state; /** do we have an active alarm? */ int have_alarm; /** our alarm */ @@ -146,7 +147,6 @@ struct grpc_subchannel_call { #define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ (((grpc_subchannel_call *)(callstack)) - 1) -static gpr_timespec compute_connect_deadline(grpc_subchannel *c); static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, bool iomgr_success); @@ -337,6 +337,22 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, grpc_closure_init(&c->connected, subchannel_connected, c); grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, "subchannel"); + gpr_backoff_init(&c->backoff_state, + GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER, + GRPC_SUBCHANNEL_RECONNECT_JITTER, + GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000, + GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + if (c->args) { + for (size_t i = 0; i < c->args->num_args; i++) { + if (0 == strcmp(c->args->args[i].key, + "grpc.testing.fixed_reconnect_backoff")) { + GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER); + gpr_backoff_init(&c->backoff_state, 1.0, 0.0, + c->args->args[i].value.integer, + c->args->args[i].value.integer); + } + } + } gpr_mu_init(&c->mu); return grpc_subchannel_index_register(exec_ctx, key, c); @@ -348,7 +364,7 @@ static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { args.interested_parties = c->pollset_set; args.addr = c->addr; args.addr_len = c->addr_len; - args.deadline = compute_connect_deadline(c); + args.deadline = c->next_attempt; args.channel_args = c->args; args.initial_connect_string = c->initial_connect_string; @@ -359,10 +375,8 @@ static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { } static void start_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { - c->backoff_delta = gpr_time_from_seconds( - GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS, GPR_TIMESPAN); c->next_attempt = - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), c->backoff_delta); + gpr_backoff_begin(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC)); continue_connect(exec_ctx, c); } @@ -577,50 +591,6 @@ static void publish_transport_locked(grpc_exec_ctx *exec_ctx, gpr_free((void *)filters); } -/* Generate a random number between 0 and 1. */ -static double generate_uniform_random_number(grpc_subchannel *c) { - c->random = (1103515245 * c->random + 12345) % ((uint32_t)1 << 31); - return c->random / (double)((uint32_t)1 << 31); -} - -/* Update backoff_delta and next_attempt in subchannel */ -static void update_reconnect_parameters(grpc_subchannel *c) { - size_t i; - int32_t backoff_delta_millis, jitter; - int32_t max_backoff_millis = - GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; - double jitter_range; - - if (c->args) { - for (i = 0; i < c->args->num_args; i++) { - if (0 == strcmp(c->args->args[i].key, - "grpc.testing.fixed_reconnect_backoff")) { - GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER); - c->next_attempt = gpr_time_add( - gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_millis(c->args->args[i].value.integer, GPR_TIMESPAN)); - return; - } - } - } - - backoff_delta_millis = - (int32_t)(gpr_time_to_millis(c->backoff_delta) * - GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER); - if (backoff_delta_millis > max_backoff_millis) { - backoff_delta_millis = max_backoff_millis; - } - c->backoff_delta = gpr_time_from_millis(backoff_delta_millis, GPR_TIMESPAN); - c->next_attempt = - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), c->backoff_delta); - - jitter_range = GRPC_SUBCHANNEL_RECONNECT_JITTER * backoff_delta_millis; - jitter = - (int32_t)((2 * generate_uniform_random_number(c) - 1) * jitter_range); - c->next_attempt = - gpr_time_add(c->next_attempt, gpr_time_from_millis(jitter, GPR_TIMESPAN)); -} - static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool iomgr_success) { grpc_subchannel *c = arg; gpr_mu_lock(&c->mu); @@ -629,7 +599,8 @@ static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool iomgr_success) { iomgr_success = 0; } if (iomgr_success) { - update_reconnect_parameters(c); + c->next_attempt = + gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC)); continue_connect(exec_ctx, c); gpr_mu_unlock(&c->mu); } else { @@ -661,17 +632,6 @@ static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); } -static gpr_timespec compute_connect_deadline(grpc_subchannel *c) { - gpr_timespec current_deadline = - gpr_time_add(c->next_attempt, c->backoff_delta); - gpr_timespec min_deadline = gpr_time_add( - gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_seconds(GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS, - GPR_TIMESPAN)); - return gpr_time_cmp(current_deadline, min_deadline) > 0 ? current_deadline - : min_deadline; -} - /* * grpc_subchannel_call implementation */ diff --git a/src/core/support/backoff.c b/src/core/support/backoff.c new file mode 100644 index 00000000000..74582196456 --- /dev/null +++ b/src/core/support/backoff.c @@ -0,0 +1,71 @@ +/* + * + * Copyright 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/support/backoff.h" + +#include + +void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter, + int64_t min_timeout_millis, int64_t max_timeout_millis) { + backoff->multiplier = multiplier; + backoff->jitter = jitter; + backoff->min_timeout_millis = min_timeout_millis; + backoff->max_timeout_millis = max_timeout_millis; + backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; +} + +gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) { + backoff->current_timeout_millis = backoff->min_timeout_millis; + return gpr_time_add( + now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN)); +} + +/* Generate a random number between 0 and 1. */ +static double generate_uniform_random_number(uint32_t *rng_state) { + *rng_state = (1103515245 * *rng_state + 12345) % ((uint32_t)1 << 31); + return *rng_state / (double)((uint32_t)1 << 31); +} + +gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) { + double new_timeout_millis = + backoff->multiplier * (double)backoff->current_timeout_millis; + double jitter_range = backoff->jitter * new_timeout_millis; + double jitter = + (2 * generate_uniform_random_number(&backoff->rng_state) - 1) * + jitter_range; + backoff->current_timeout_millis = + GPR_CLAMP((int64_t)(new_timeout_millis + jitter), + backoff->min_timeout_millis, backoff->max_timeout_millis); + return gpr_time_add( + now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN)); +} diff --git a/src/core/support/backoff.h b/src/core/support/backoff.h new file mode 100644 index 00000000000..3234aa214d4 --- /dev/null +++ b/src/core/support/backoff.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 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. + * + */ + +#ifndef GRPC_INTERNAL_CORE_SUPPORT_BACKOFF_H +#define GRPC_INTERNAL_CORE_SUPPORT_BACKOFF_H + +#include + +typedef struct { + /// const: multiplier between retry attempts + double multiplier; + /// const: amount to randomize backoffs + double jitter; + /// const: minimum time between retries in milliseconds + int64_t min_timeout_millis; + /// const: maximum time between retries in milliseconds + int64_t max_timeout_millis; + + /// random number generator + uint32_t rng_state; + + /// current retry timeout in milliseconds + int64_t current_timeout_millis; +} gpr_backoff; + +/// Initialize backoff machinery - does not need to be destroyed +void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter, + int64_t min_timeout_millis, int64_t max_timeout_millis); + +/// Begin retry loop: returns a timespec for the NEXT retry +gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now); +/// Step a retry loop: returns a timespec for the NEXT retry +gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now); + +#endif // GRPC_INTERNAL_CORE_SUPPORT_BACKOFF_H diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index a543791f5ce..31e16e04912 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -34,6 +34,7 @@ CORE_SOURCE_FILES = [ 'src/core/profiling/stap_timers.c', 'src/core/support/alloc.c', 'src/core/support/avl.c', + 'src/core/support/backoff.c', 'src/core/support/cmdline.c', 'src/core/support/cpu_iphone.c', 'src/core/support/cpu_linux.c', diff --git a/test/core/support/backoff_test.c b/test/core/support/backoff_test.c new file mode 100644 index 00000000000..870b60b2d5a --- /dev/null +++ b/test/core/support/backoff_test.c @@ -0,0 +1,107 @@ +/* + * + * Copyright 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/support/backoff.h" + +#include + +#include "test/core/util/test_config.h" + +static void test_constant_backoff(void) { + gpr_backoff backoff; + gpr_backoff_init(&backoff, 1.0, 0.0, 1000, 1000); + + gpr_timespec now = gpr_time_0(GPR_TIMESPAN); + gpr_timespec next = gpr_backoff_begin(&backoff, now); + GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 1000); + for (int i = 0; i < 10000; i++) { + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_to_millis(gpr_time_sub(next, now)) == 1000); + now = next; + } +} + +static void test_no_jitter_backoff(void) { + gpr_backoff backoff; + gpr_backoff_init(&backoff, 2.0, 0.0, 1, 513); + + gpr_timespec now = gpr_time_0(GPR_TIMESPAN); + gpr_timespec next = gpr_backoff_begin(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(3, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(7, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(15, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(31, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(63, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(127, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(255, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(511, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1023, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(1536, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2049, GPR_TIMESPAN), next) == 0); + now = next; + next = gpr_backoff_step(&backoff, now); + GPR_ASSERT(gpr_time_cmp(gpr_time_from_millis(2562, GPR_TIMESPAN), next) == 0); +} + +int main(int argc, char **argv) { + grpc_test_init(argc, argv); + gpr_time_init(); + + test_constant_backoff(); + test_no_jitter_backoff(); + + return 0; +} diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 71b7af2a227..a06d4ecb426 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1112,6 +1112,7 @@ include/grpc/impl/codegen/sync_posix.h \ include/grpc/impl/codegen/sync_win32.h \ include/grpc/impl/codegen/time.h \ src/core/profiling/timers.h \ +src/core/support/backoff.h \ src/core/support/block_annotate.h \ src/core/support/env.h \ src/core/support/load_file.h \ @@ -1126,6 +1127,7 @@ src/core/profiling/basic_timers.c \ src/core/profiling/stap_timers.c \ src/core/support/alloc.c \ src/core/support/avl.c \ +src/core/support/backoff.c \ src/core/support/cmdline.c \ src/core/support/cpu_iphone.c \ src/core/support/cpu_linux.c \ diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index 783a353ca84..503adba2fbf 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -404,6 +404,20 @@ "third_party": false, "type": "target" }, + { + "deps": [ + "gpr", + "gpr_test_util" + ], + "headers": [], + "language": "c", + "name": "gpr_backoff_test", + "src": [ + "test/core/support/backoff_test.c" + ], + "third_party": false, + "type": "target" + }, { "deps": [ "gpr", @@ -3746,6 +3760,7 @@ "include/grpc/support/tls_pthread.h", "include/grpc/support/useful.h", "src/core/profiling/timers.h", + "src/core/support/backoff.h", "src/core/support/block_annotate.h", "src/core/support/env.h", "src/core/support/load_file.h", @@ -3807,6 +3822,8 @@ "src/core/profiling/timers.h", "src/core/support/alloc.c", "src/core/support/avl.c", + "src/core/support/backoff.c", + "src/core/support/backoff.h", "src/core/support/block_annotate.h", "src/core/support/cmdline.c", "src/core/support/cpu_iphone.c", diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 2be7d8a48a8..784dfd865bd 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -453,6 +453,27 @@ "windows" ] }, + { + "args": [], + "ci_platforms": [ + "linux", + "mac", + "posix", + "windows" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "gpr_backoff_test", + "platforms": [ + "linux", + "mac", + "posix", + "windows" + ] + }, { "args": [], "ci_platforms": [ @@ -2295,7 +2316,7 @@ "mac", "posix" ], - "cpu_cost": 10, + "cpu_cost": 0.5, "exclude_configs": [], "flaky": false, "gtest": false, diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln index 2092162f61b..752fac74838 100644 --- a/vsprojects/buildtests_c.sln +++ b/vsprojects/buildtests_c.sln @@ -334,6 +334,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_avl_test", "vcxproj\tes {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_backoff_test", "vcxproj\test\gpr_backoff_test\gpr_backoff_test.vcxproj", "{889F570D-E046-BD52-9E4C-B4CD13DFE2DB}" + ProjectSection(myProperties) = preProject + lib = "False" + EndProjectSection + ProjectSection(ProjectDependencies) = postProject + {EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037} + {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gpr_cmdline_test", "vcxproj\test\gpr_cmdline_test\gpr_cmdline_test.vcxproj", "{10668A5D-65CD-F530-22D0-747B395B4C26}" ProjectSection(myProperties) = preProject lib = "False" @@ -1868,6 +1877,22 @@ Global {144D8CFF-2737-A18A-DCFD-01603533D63F}.Release-DLL|Win32.Build.0 = Release|Win32 {144D8CFF-2737-A18A-DCFD-01603533D63F}.Release-DLL|x64.ActiveCfg = Release|x64 {144D8CFF-2737-A18A-DCFD-01603533D63F}.Release-DLL|x64.Build.0 = Release|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug|Win32.ActiveCfg = Debug|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug|x64.ActiveCfg = Debug|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release|Win32.ActiveCfg = Release|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release|x64.ActiveCfg = Release|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug|Win32.Build.0 = Debug|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug|x64.Build.0 = Debug|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release|Win32.Build.0 = Release|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release|x64.Build.0 = Release|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug-DLL|Win32.ActiveCfg = Debug|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug-DLL|Win32.Build.0 = Debug|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug-DLL|x64.ActiveCfg = Debug|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Debug-DLL|x64.Build.0 = Debug|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release-DLL|Win32.ActiveCfg = Release|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release-DLL|Win32.Build.0 = Release|Win32 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release-DLL|x64.ActiveCfg = Release|x64 + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB}.Release-DLL|x64.Build.0 = Release|x64 {10668A5D-65CD-F530-22D0-747B395B4C26}.Debug|Win32.ActiveCfg = Debug|Win32 {10668A5D-65CD-F530-22D0-747B395B4C26}.Debug|x64.ActiveCfg = Debug|x64 {10668A5D-65CD-F530-22D0-747B395B4C26}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/vsprojects/vcxproj/gpr/gpr.vcxproj b/vsprojects/vcxproj/gpr/gpr.vcxproj index dae8e623d82..9281fa39952 100644 --- a/vsprojects/vcxproj/gpr/gpr.vcxproj +++ b/vsprojects/vcxproj/gpr/gpr.vcxproj @@ -192,6 +192,7 @@ + @@ -212,6 +213,8 @@ + + diff --git a/vsprojects/vcxproj/gpr/gpr.vcxproj.filters b/vsprojects/vcxproj/gpr/gpr.vcxproj.filters index 055b29f6480..b85060f3850 100644 --- a/vsprojects/vcxproj/gpr/gpr.vcxproj.filters +++ b/vsprojects/vcxproj/gpr/gpr.vcxproj.filters @@ -13,6 +13,9 @@ src\core\support + + src\core\support + src\core\support @@ -263,6 +266,9 @@ src\core\profiling + + src\core\support + src\core\support diff --git a/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj b/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj new file mode 100644 index 00000000000..6aa292ef4f5 --- /dev/null +++ b/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj @@ -0,0 +1,193 @@ + + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {889F570D-E046-BD52-9E4C-B4CD13DFE2DB} + true + $(SolutionDir)IntDir\$(MSBuildProjectName)\ + + + + v100 + + + v110 + + + v120 + + + v140 + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + gpr_backoff_test + static + Debug + static + Debug + + + gpr_backoff_test + static + Release + static + Release + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + true + None + false + + + Console + true + false + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + true + None + false + + + Console + true + false + + + + + + NotUsing + Level3 + MaxSpeed + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + true + true + MultiThreaded + true + None + false + + + Console + true + false + true + true + + + + + + NotUsing + Level3 + MaxSpeed + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + true + true + MultiThreaded + true + None + false + + + Console + true + false + true + true + + + + + + + + + + {EAB0A629-17A9-44DB-B5FF-E91A721FE037} + + + {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + diff --git a/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj.filters b/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj.filters new file mode 100644 index 00000000000..eb3c1bbd8c2 --- /dev/null +++ b/vsprojects/vcxproj/test/gpr_backoff_test/gpr_backoff_test.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + test\core\support + + + + + + {4b7f1d25-d344-0bcb-63d8-2ba959874ea8} + + + {2bd2fba5-8799-2c78-469f-ec3ba6b01da8} + + + {2ef0cfa7-fe3d-2b82-7d0e-f9e293e8f98c} + + + +