diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index 28db2db0b48..c01babece16 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -163,6 +163,9 @@ typedef struct { /** Maximum message length that the channel can send. Int valued, bytes. -1 means unlimited. */ #define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length" +/** Maximum time that a channel may have no outstanding rpcs. Int valued, + seconds. INT_MAX means unlimited. */ +#define GPRC_ARG_MAX_CONNECION_IDLE_S "grpc.max_connection_idle" /** Maximum time that a channel may exist. Int valued, seconds. INT_MAX means unlimited. */ #define GPRC_ARG_MAX_CONNECION_AGE_S "grpc.max_connection_age" diff --git a/src/core/lib/channel/max_age_filter.c b/src/core/lib/channel/max_age_filter.c index 364e01705d2..928bb96138d 100644 --- a/src/core/lib/channel/max_age_filter.c +++ b/src/core/lib/channel/max_age_filter.c @@ -42,37 +42,76 @@ #define DEFAULT_MAX_CONNECTION_AGE_S INT_MAX #define DEFAULT_MAX_CONNECTION_AGE_GRACE_S INT_MAX +#define DEFAULT_MAX_CONNECTION_IDLE_S INT_MAX typedef struct channel_data { // We take a reference to the channel stack for the timer callback grpc_channel_stack* channel_stack; - // Guards access to max_age_timer and max_age_timer_pending + // Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer + // and max_age_grace_timer_pending gpr_mu max_age_timer_mu; // True if the max_age timer callback is currently pending bool max_age_timer_pending; - // True if the max_age timer callback is currently pending + // True if the max_age_grace timer callback is currently pending bool max_age_grace_timer_pending; // The timer for checking if the channel has reached its max age grpc_timer max_age_timer; - // The timer for checking if the channel has reached its max age + // The timer for checking if the max-aged channel has uesed up the grace + // period grpc_timer max_age_grace_timer; + // The timer for checking if the channel's idle duration reaches + // max_connection_idle + grpc_timer max_idle_timer; + // Allowed max time a channel may have no outstanding rpcs + gpr_timespec max_connection_idle; // Allowed max time a channel may exist gpr_timespec max_connection_age; // Allowed grace period after the channel reaches its max age gpr_timespec max_connection_age_grace; + // Closure to run when the channel's idle duration reaches max_connection_idle + // and should be closed gracefully + grpc_closure close_max_idle_channel; // Closure to run when the channel reaches its max age and should be closed // gracefully grpc_closure close_max_age_channel; // Closure to run the channel uses up its max age grace time and should be // closed forcibly grpc_closure force_close_max_age_channel; + // Closure to run when the init fo channel stack is done and the max_idle + // timer should be started + grpc_closure start_max_idle_timer_after_init; // Closure to run when the init fo channel stack is done and the max_age timer // should be started grpc_closure start_max_age_timer_after_init; // Closure to run when the goaway op is finished and the max_age_timer grpc_closure start_max_age_grace_timer_after_goaway_op; + // Number of active calls + gpr_atm call_count; } channel_data; +static void increase_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { + if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) { + grpc_timer_cancel(exec_ctx, &chand->max_idle_timer); + } +} + +static void decrease_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { + if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) { + grpc_timer_init( + exec_ctx, &chand->max_idle_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_idle), + &chand->close_max_idle_channel, gpr_now(GPR_CLOCK_MONOTONIC)); + } +} + +static void start_max_idle_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = arg; + decrease_call_count(exec_ctx, chand); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age start_max_idle_timer_after_init"); +} + static void start_max_age_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { channel_data* chand = arg; @@ -103,6 +142,23 @@ static void start_max_age_grace_timer_after_goaway_op(grpc_exec_ctx* exec_ctx, "max_age start_max_age_grace_timer_after_goaway_op"); } +static void close_max_idle_channel(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = arg; + gpr_atm_no_barrier_fetch_add(&chand->call_count, 1); + if (error == GRPC_ERROR_NONE) { + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->goaway_error = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(exec_ctx, elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("close_max_idle_channel", error); + } +} + static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { channel_data* chand = arg; @@ -147,7 +203,8 @@ static void force_close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, const grpc_call_element_args* args) { - // call_num ++; + channel_data* chand = elem->channel_data; + increase_call_count(exec_ctx, chand); return GRPC_ERROR_NONE; } @@ -155,7 +212,8 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, const grpc_call_final_info* final_info, grpc_closure* ignored) { - // call_num --; + channel_data* chand = elem->channel_data; + decrease_call_count(exec_ctx, chand); } // Constructor for channel_data. @@ -171,11 +229,15 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, DEFAULT_MAX_CONNECTION_AGE_S == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_AGE_S, GPR_TIMESPAN); - chand->max_connection_age = + chand->max_connection_age_grace = DEFAULT_MAX_CONNECTION_AGE_GRACE_S == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_AGE_GRACE_S, GPR_TIMESPAN); + chand->max_connection_idle = + DEFAULT_MAX_CONNECTION_IDLE_S == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_IDLE_S, GPR_TIMESPAN); for (size_t i = 0; i < args->channel_args->num_args; ++i) { if (0 == strcmp(args->channel_args->args[i].key, GPRC_ARG_MAX_CONNECION_AGE_S)) { @@ -189,18 +251,31 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, GPRC_ARG_MAX_CONNECION_AGE_GRACE_S)) { const int value = grpc_channel_arg_get_integer( &args->channel_args->args[i], - (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_S, 1, + (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_S, 0, INT_MAX}); chand->max_connection_age_grace = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(value, GPR_TIMESPAN); + } else if (0 == strcmp(args->channel_args->args[i].key, + GPRC_ARG_MAX_CONNECION_IDLE_S)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], + (grpc_integer_options){DEFAULT_MAX_CONNECTION_IDLE_S, 1, INT_MAX}); + chand->max_connection_age_grace = + value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_seconds(value, GPR_TIMESPAN); } } + grpc_closure_init(&chand->close_max_idle_channel, close_max_idle_channel, + chand, grpc_schedule_on_exec_ctx); grpc_closure_init(&chand->close_max_age_channel, close_max_age_channel, chand, grpc_schedule_on_exec_ctx); grpc_closure_init(&chand->force_close_max_age_channel, force_close_max_age_channel, chand, grpc_schedule_on_exec_ctx); + grpc_closure_init(&chand->start_max_idle_timer_after_init, + start_max_idle_timer_after_init, chand, + grpc_schedule_on_exec_ctx); grpc_closure_init(&chand->start_max_age_timer_after_init, start_max_age_timer_after_init, chand, grpc_schedule_on_exec_ctx); @@ -223,6 +298,14 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, GRPC_ERROR_NONE); } + gpr_atm_rel_store(&chand->call_count, 1); + if (gpr_time_cmp(chand->max_connection_idle, gpr_inf_future(GPR_TIMESPAN)) != + 0) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_idle_timer_after_init"); + grpc_closure_sched(exec_ctx, &chand->start_max_idle_timer_after_init, + GRPC_ERROR_NONE); + } return GRPC_ERROR_NONE; } @@ -238,6 +321,7 @@ static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, grpc_timer_cancel(exec_ctx, &chand->max_age_grace_timer); } gpr_mu_unlock(&chand->max_age_timer_mu); + increase_call_count(exec_ctx, chand); } const grpc_channel_filter grpc_max_age_filter = {