mirror of https://github.com/grpc/grpc.git
parent
0581d129f5
commit
577c9b2f11
19 changed files with 1085 additions and 1181 deletions
@ -0,0 +1,282 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, 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/channel/subchannel_call_holder.h" |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
|
||||||
|
#include "src/core/profiling/timers.h" |
||||||
|
|
||||||
|
#define GET_CALL(holder) \ |
||||||
|
((grpc_subchannel_call *)(gpr_atm_acq_load(&(holder)->subchannel_call))) |
||||||
|
|
||||||
|
#define CANCELLED_CALL ((grpc_subchannel_call *)1) |
||||||
|
|
||||||
|
static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *holder, |
||||||
|
int success); |
||||||
|
static void call_ready(grpc_exec_ctx *exec_ctx, void *holder, int success); |
||||||
|
static void retry_ops(grpc_exec_ctx *exec_ctx, void *retry_ops_args, |
||||||
|
int success); |
||||||
|
|
||||||
|
static void add_waiting_locked(grpc_subchannel_call_holder *holder, |
||||||
|
grpc_transport_stream_op *op); |
||||||
|
static void fail_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder); |
||||||
|
static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder); |
||||||
|
|
||||||
|
void grpc_subchannel_call_holder_init( |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_subchannel_call_holder_pick_subchannel pick_subchannel, |
||||||
|
void *pick_subchannel_arg) { |
||||||
|
gpr_atm_rel_store(&holder->subchannel_call, 0); |
||||||
|
holder->pick_subchannel = pick_subchannel; |
||||||
|
holder->pick_subchannel_arg = pick_subchannel_arg; |
||||||
|
gpr_mu_init(&holder->mu); |
||||||
|
holder->subchannel = NULL; |
||||||
|
holder->waiting_ops = NULL; |
||||||
|
holder->waiting_ops_count = 0; |
||||||
|
holder->waiting_ops_capacity = 0; |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder) { |
||||||
|
grpc_subchannel_call *call = GET_CALL(holder); |
||||||
|
if (call != NULL && call != CANCELLED_CALL) { |
||||||
|
GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "holder"); |
||||||
|
} |
||||||
|
GPR_ASSERT(holder->creation_phase == |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING); |
||||||
|
gpr_mu_destroy(&holder->mu); |
||||||
|
GPR_ASSERT(holder->waiting_ops_count == 0); |
||||||
|
gpr_free(holder->waiting_ops); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_transport_stream_op *op) { |
||||||
|
/* try to (atomically) get the call */ |
||||||
|
grpc_subchannel_call *call = GET_CALL(holder); |
||||||
|
GPR_TIMER_BEGIN("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
if (call == CANCELLED_CALL) { |
||||||
|
grpc_transport_stream_op_finish_with_failure(exec_ctx, op); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (call != NULL) { |
||||||
|
grpc_subchannel_call_process_op(exec_ctx, call, op); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
/* we failed; lock and figure out what to do */ |
||||||
|
gpr_mu_lock(&holder->mu); |
||||||
|
retry: |
||||||
|
/* need to recheck that another thread hasn't set the call */ |
||||||
|
call = GET_CALL(holder); |
||||||
|
if (call == CANCELLED_CALL) { |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
grpc_transport_stream_op_finish_with_failure(exec_ctx, op); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (call != NULL) { |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
grpc_subchannel_call_process_op(exec_ctx, call, op); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
/* if this is a cancellation, then we can raise our cancelled flag */ |
||||||
|
if (op->cancel_with_status != GRPC_STATUS_OK) { |
||||||
|
if (!gpr_atm_rel_cas(&holder->subchannel_call, 0, 1)) { |
||||||
|
goto retry; |
||||||
|
} else { |
||||||
|
switch (holder->creation_phase) { |
||||||
|
case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING: |
||||||
|
break; |
||||||
|
case GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL: |
||||||
|
grpc_subchannel_cancel_create_call(exec_ctx, holder->subchannel, |
||||||
|
&holder->subchannel_call); |
||||||
|
break; |
||||||
|
case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL: |
||||||
|
holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL, |
||||||
|
&holder->subchannel, NULL); |
||||||
|
break; |
||||||
|
} |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
grpc_transport_stream_op_finish_with_failure(exec_ctx, op); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
/* if we don't have a subchannel, try to get one */ |
||||||
|
if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && |
||||||
|
holder->subchannel == NULL && op->send_initial_metadata != NULL) { |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL; |
||||||
|
grpc_closure_init(&holder->next_step, subchannel_ready, holder); |
||||||
|
if (holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, |
||||||
|
op->send_initial_metadata, &holder->subchannel, |
||||||
|
&holder->next_step)) { |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
} |
||||||
|
} |
||||||
|
/* if we've got a subchannel, then let's ask it to create a call */ |
||||||
|
if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && |
||||||
|
holder->subchannel != NULL) { |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL; |
||||||
|
grpc_closure_init(&holder->next_step, call_ready, holder); |
||||||
|
if (grpc_subchannel_create_call(exec_ctx, holder->subchannel, |
||||||
|
holder->pollset, &holder->subchannel_call, |
||||||
|
&holder->next_step)) { |
||||||
|
/* got one immediately - continue the op (and any waiting ops) */ |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
retry_waiting_locked(exec_ctx, holder); |
||||||
|
goto retry; |
||||||
|
} |
||||||
|
} |
||||||
|
/* nothing to be done but wait */ |
||||||
|
add_waiting_locked(holder, op); |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); |
||||||
|
} |
||||||
|
|
||||||
|
static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, int success) { |
||||||
|
grpc_subchannel_call_holder *holder = arg; |
||||||
|
grpc_subchannel_call *call; |
||||||
|
gpr_mu_lock(&holder->mu); |
||||||
|
GPR_ASSERT(holder->creation_phase == |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL); |
||||||
|
call = GET_CALL(holder); |
||||||
|
GPR_ASSERT(call == NULL || call == CANCELLED_CALL); |
||||||
|
if (holder->subchannel == NULL) { |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
fail_locked(exec_ctx, holder); |
||||||
|
} else { |
||||||
|
grpc_closure_init(&holder->next_step, call_ready, holder); |
||||||
|
if (grpc_subchannel_create_call(exec_ctx, holder->subchannel, |
||||||
|
holder->pollset, &holder->subchannel_call, |
||||||
|
&holder->next_step)) { |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
/* got one immediately - continue the op (and any waiting ops) */ |
||||||
|
retry_waiting_locked(exec_ctx, holder); |
||||||
|
} |
||||||
|
} |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
} |
||||||
|
|
||||||
|
static void call_ready(grpc_exec_ctx *exec_ctx, void *arg, int success) { |
||||||
|
grpc_subchannel_call_holder *holder = arg; |
||||||
|
GPR_TIMER_BEGIN("call_ready", 0); |
||||||
|
gpr_mu_lock(&holder->mu); |
||||||
|
GPR_ASSERT(holder->creation_phase == |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL); |
||||||
|
holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; |
||||||
|
if (GET_CALL(holder) != NULL) { |
||||||
|
retry_waiting_locked(exec_ctx, holder); |
||||||
|
} else { |
||||||
|
fail_locked(exec_ctx, holder); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&holder->mu); |
||||||
|
GPR_TIMER_END("call_ready", 0); |
||||||
|
} |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
grpc_transport_stream_op *ops; |
||||||
|
size_t nops; |
||||||
|
grpc_subchannel_call *call; |
||||||
|
} retry_ops_args; |
||||||
|
|
||||||
|
static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder) { |
||||||
|
retry_ops_args *a = gpr_malloc(sizeof(*a)); |
||||||
|
a->ops = holder->waiting_ops; |
||||||
|
a->nops = holder->waiting_ops_count; |
||||||
|
a->call = GET_CALL(holder); |
||||||
|
if (a->call == CANCELLED_CALL) { |
||||||
|
gpr_free(a); |
||||||
|
fail_locked(exec_ctx, holder); |
||||||
|
return; |
||||||
|
} |
||||||
|
holder->waiting_ops = NULL; |
||||||
|
holder->waiting_ops_count = 0; |
||||||
|
holder->waiting_ops_capacity = 0; |
||||||
|
GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops"); |
||||||
|
grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(retry_ops, a), 1); |
||||||
|
} |
||||||
|
|
||||||
|
static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, int success) { |
||||||
|
retry_ops_args *a = args; |
||||||
|
size_t i; |
||||||
|
for (i = 0; i < a->nops; i++) { |
||||||
|
grpc_subchannel_call_process_op(exec_ctx, a->call, &a->ops[i]); |
||||||
|
} |
||||||
|
GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, a->call, "retry_ops"); |
||||||
|
gpr_free(a->ops); |
||||||
|
gpr_free(a); |
||||||
|
} |
||||||
|
|
||||||
|
static void add_waiting_locked(grpc_subchannel_call_holder *holder, |
||||||
|
grpc_transport_stream_op *op) { |
||||||
|
GPR_TIMER_BEGIN("add_waiting_locked", 0); |
||||||
|
if (holder->waiting_ops_count == holder->waiting_ops_capacity) { |
||||||
|
holder->waiting_ops_capacity = GPR_MAX(3, 2 * holder->waiting_ops_capacity); |
||||||
|
holder->waiting_ops = |
||||||
|
gpr_realloc(holder->waiting_ops, holder->waiting_ops_capacity * |
||||||
|
sizeof(*holder->waiting_ops)); |
||||||
|
} |
||||||
|
holder->waiting_ops[holder->waiting_ops_count++] = *op; |
||||||
|
GPR_TIMER_END("add_waiting_locked", 0); |
||||||
|
} |
||||||
|
|
||||||
|
static void fail_locked(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder) { |
||||||
|
size_t i; |
||||||
|
for (i = 0; i < holder->waiting_ops_count; i++) { |
||||||
|
grpc_exec_ctx_enqueue(exec_ctx, holder->waiting_ops[i].on_complete, 0); |
||||||
|
grpc_exec_ctx_enqueue(exec_ctx, holder->waiting_ops[i].recv_message_ready, |
||||||
|
0); |
||||||
|
} |
||||||
|
holder->waiting_ops_count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_channel *master) { |
||||||
|
grpc_subchannel_call *subchannel_call = GET_CALL(holder); |
||||||
|
|
||||||
|
if (subchannel_call) { |
||||||
|
return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call); |
||||||
|
} else { |
||||||
|
return grpc_channel_get_target(master); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2015, 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_CHANNEL_SUBCHANNEL_CALL_HOLDER_H |
||||||
|
#define GRPC_INTERNAL_CORE_CHANNEL_SUBCHANNEL_CALL_HOLDER_H |
||||||
|
|
||||||
|
#include "src/core/client_config/subchannel.h" |
||||||
|
|
||||||
|
typedef int (*grpc_subchannel_call_holder_pick_subchannel)( |
||||||
|
grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata, |
||||||
|
grpc_subchannel **subchannel, grpc_closure *on_ready); |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING, |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL, |
||||||
|
GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL |
||||||
|
} grpc_subchannel_call_holder_creation_phase; |
||||||
|
|
||||||
|
typedef struct grpc_subchannel_call_holder { |
||||||
|
/* either 0 for no call, 1 for cancelled, or a pointer to a
|
||||||
|
grpc_subchannel_call */ |
||||||
|
gpr_atm subchannel_call; |
||||||
|
grpc_subchannel_call_holder_pick_subchannel pick_subchannel; |
||||||
|
void *pick_subchannel_arg; |
||||||
|
|
||||||
|
gpr_mu mu; |
||||||
|
|
||||||
|
grpc_subchannel_call_holder_creation_phase creation_phase; |
||||||
|
grpc_subchannel *subchannel; |
||||||
|
grpc_pollset *pollset; |
||||||
|
|
||||||
|
grpc_transport_stream_op *waiting_ops; |
||||||
|
size_t waiting_ops_count; |
||||||
|
size_t waiting_ops_capacity; |
||||||
|
|
||||||
|
grpc_closure next_step; |
||||||
|
} grpc_subchannel_call_holder; |
||||||
|
|
||||||
|
void grpc_subchannel_call_holder_init( |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_subchannel_call_holder_pick_subchannel pick_subchannel, |
||||||
|
void *pick_subchannel_arg); |
||||||
|
void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder); |
||||||
|
|
||||||
|
void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_transport_stream_op *op); |
||||||
|
char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx, |
||||||
|
grpc_subchannel_call_holder *holder, |
||||||
|
grpc_channel *master); |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue