aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Mark D. Roth <roth@google.com>2016-09-01 08:20:27 -0700
committerGravatar Mark D. Roth <roth@google.com>2016-09-01 08:20:27 -0700
commit2a5959f2aeca2e86cc752288d890bc10b24c55d0 (patch)
treed95da0b4b9b5987f4932496a16fe4ec96ecdc29d /src/core
parent4c0fe49f76e0e3d7e7ceed5722cc43010af61ab3 (diff)
A bit more cleanup.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/ext/client_config/client_channel.c655
1 files changed, 323 insertions, 332 deletions
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index e4d7527ce8..fff80abb80 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -54,54 +54,9 @@
/* Client channel implementation */
-#define GET_CALL(call_data) \
- ((grpc_subchannel_call *)(gpr_atm_acq_load(&(call_data)->subchannel_call)))
-
-#define CANCELLED_CALL ((grpc_subchannel_call *)1)
-
-/** Picks a subchannel.
- Returns true if subchannel is available immediately (in which case on_ready
- should not be called), or false otherwise (in which case on_ready should be
- called when the subchannel is available) */
-typedef bool (*pick_subchannel_cb)(
- grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata,
- uint32_t initial_metadata_flags,
- grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready);
-
-typedef enum {
- GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING,
- GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL
-} subchannel_creation_phase;
-
-/** Call data. Holds a pointer to grpc_subchannel_call and the
- associated machinery to create such a pointer.
- Handles queueing of stream ops until a call object is ready, waiting
- for initial metadata before trying to create a call object,
- and handling cancellation gracefully. */
-typedef struct client_channel_call_data {
- /** either 0 for no call, 1 for cancelled, or a pointer to a
- grpc_subchannel_call */
- gpr_atm subchannel_call;
- /** Helper function to choose the subchannel on which to create
- the call object. Channel filter delegates to the load
- balancing policy (once it's ready). */
- pick_subchannel_cb pick_subchannel;
- void *pick_subchannel_arg;
-
- gpr_mu mu;
-
- subchannel_creation_phase creation_phase;
- grpc_connected_subchannel *connected_subchannel;
- grpc_polling_entity *pollent;
-
- grpc_transport_stream_op *waiting_ops;
- size_t waiting_ops_count;
- size_t waiting_ops_capacity;
-
- grpc_closure next_step;
-
- grpc_call_stack *owning_call;
-} call_data;
+/*************************************************************************
+ * CHANNEL-WIDE FUNCTIONS
+ */
typedef struct client_channel_channel_data {
/** resolver for this channel */
@@ -140,215 +95,6 @@ typedef struct {
grpc_lb_policy *lb_policy;
} lb_policy_connectivity_watcher;
-typedef struct {
- grpc_closure closure;
- grpc_call_element *elem;
-} waiting_call;
-
-static void add_waiting_locked(call_data *calld, grpc_transport_stream_op *op) {
- GPR_TIMER_BEGIN("add_waiting_locked", 0);
- if (calld->waiting_ops_count == calld->waiting_ops_capacity) {
- calld->waiting_ops_capacity = GPR_MAX(3, 2 * calld->waiting_ops_capacity);
- calld->waiting_ops =
- gpr_realloc(calld->waiting_ops,
- calld->waiting_ops_capacity * sizeof(*calld->waiting_ops));
- }
- calld->waiting_ops[calld->waiting_ops_count++] = *op;
- GPR_TIMER_END("add_waiting_locked", 0);
-}
-
-static void fail_locked(grpc_exec_ctx *exec_ctx, call_data *calld,
- grpc_error *error) {
- size_t i;
- for (i = 0; i < calld->waiting_ops_count; i++) {
- grpc_transport_stream_op_finish_with_failure(
- exec_ctx, &calld->waiting_ops[i], GRPC_ERROR_REF(error));
- }
- calld->waiting_ops_count = 0;
- GRPC_ERROR_UNREF(error);
-}
-
-typedef struct {
- grpc_transport_stream_op *ops;
- size_t nops;
- grpc_subchannel_call *call;
-} retry_ops_args;
-
-static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
- 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 retry_waiting_locked(grpc_exec_ctx *exec_ctx, call_data *calld) {
- retry_ops_args *a = gpr_malloc(sizeof(*a));
- a->ops = calld->waiting_ops;
- a->nops = calld->waiting_ops_count;
- a->call = GET_CALL(calld);
- if (a->call == CANCELLED_CALL) {
- gpr_free(a);
- fail_locked(exec_ctx, calld, GRPC_ERROR_CANCELLED);
- return;
- }
- calld->waiting_ops = NULL;
- calld->waiting_ops_count = 0;
- calld->waiting_ops_capacity = 0;
- GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
- grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(retry_ops, a),
- GRPC_ERROR_NONE, NULL);
-}
-
-static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
- grpc_error *error) {
- call_data *calld = arg;
- gpr_mu_lock(&calld->mu);
- GPR_ASSERT(calld->creation_phase ==
- GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
- calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
- if (calld->connected_subchannel == NULL) {
- gpr_atm_no_barrier_store(&calld->subchannel_call, 1);
- fail_locked(exec_ctx, calld, GRPC_ERROR_CREATE_REFERENCING(
- "Failed to create subchannel", &error, 1));
- } else if (1 == gpr_atm_acq_load(&calld->subchannel_call)) {
- /* already cancelled before subchannel became ready */
- fail_locked(exec_ctx, calld,
- GRPC_ERROR_CREATE_REFERENCING(
- "Cancelled before creating subchannel", &error, 1));
- } else {
- grpc_subchannel_call *subchannel_call = NULL;
- grpc_error *new_error = grpc_connected_subchannel_create_call(
- exec_ctx, calld->connected_subchannel, calld->pollent,
- &subchannel_call);
- if (new_error != GRPC_ERROR_NONE) {
- new_error = grpc_error_add_child(new_error, error);
- subchannel_call = CANCELLED_CALL;
- fail_locked(exec_ctx, calld, new_error);
- }
- gpr_atm_rel_store(&calld->subchannel_call,
- (gpr_atm)(uintptr_t)subchannel_call);
- retry_waiting_locked(exec_ctx, calld);
- }
- gpr_mu_unlock(&calld->mu);
- GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
-}
-
-static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
- call_data *calld = elem->call_data;
- grpc_subchannel_call *subchannel_call = GET_CALL(calld);
- if (subchannel_call == NULL || subchannel_call == CANCELLED_CALL) {
- return NULL;
- } else {
- return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call);
- }
-}
-
-// The logic here is fairly complicated, due to (a) the fact that we
-// need to handle the case where we receive the send op before the
-// initial metadata op, and (b) the need for efficiency, especially in
-// the streaming case.
-// TODO(ctiller): Explain this more thoroughly.
-static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
- grpc_call_element *elem,
- grpc_transport_stream_op *op) {
- call_data *calld = elem->call_data;
- GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
- /* try to (atomically) get the call */
- grpc_subchannel_call *call = GET_CALL(calld);
- GPR_TIMER_BEGIN("cc_start_transport_stream_op", 0);
- if (call == CANCELLED_CALL) {
- grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
- GRPC_ERROR_CANCELLED);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
- return;
- }
- if (call != NULL) {
- grpc_subchannel_call_process_op(exec_ctx, call, op);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
- return;
- }
- /* we failed; lock and figure out what to do */
- gpr_mu_lock(&calld->mu);
-retry:
- /* need to recheck that another thread hasn't set the call */
- call = GET_CALL(calld);
- if (call == CANCELLED_CALL) {
- gpr_mu_unlock(&calld->mu);
- grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
- GRPC_ERROR_CANCELLED);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
- return;
- }
- if (call != NULL) {
- gpr_mu_unlock(&calld->mu);
- grpc_subchannel_call_process_op(exec_ctx, call, op);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
- return;
- }
- /* if this is a cancellation, then we can raise our cancelled flag */
- if (op->cancel_error != GRPC_ERROR_NONE) {
- if (!gpr_atm_rel_cas(&calld->subchannel_call, 0,
- (gpr_atm)(uintptr_t)CANCELLED_CALL)) {
- goto retry;
- } else {
- switch (calld->creation_phase) {
- case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
- fail_locked(exec_ctx, calld, GRPC_ERROR_REF(op->cancel_error));
- break;
- case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
- calld->pick_subchannel(exec_ctx, calld->pick_subchannel_arg, NULL, 0,
- &calld->connected_subchannel, NULL);
- break;
- }
- gpr_mu_unlock(&calld->mu);
- grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
- GRPC_ERROR_CANCELLED);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
- return;
- }
- }
- /* if we don't have a subchannel, try to get one */
- if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
- calld->connected_subchannel == NULL &&
- op->send_initial_metadata != NULL) {
- calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
- grpc_closure_init(&calld->next_step, subchannel_ready, calld);
- GRPC_CALL_STACK_REF(calld->owning_call, "pick_subchannel");
- if (calld->pick_subchannel(
- exec_ctx, calld->pick_subchannel_arg, op->send_initial_metadata,
- op->send_initial_metadata_flags, &calld->connected_subchannel,
- &calld->next_step)) {
- calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
- GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
- }
- }
- /* if we've got a subchannel, then let's ask it to create a call */
- if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
- calld->connected_subchannel != NULL) {
- grpc_subchannel_call *subchannel_call = NULL;
- grpc_error *error = grpc_connected_subchannel_create_call(
- exec_ctx, calld->connected_subchannel, calld->pollent,
- &subchannel_call);
- if (error != GRPC_ERROR_NONE) {
- subchannel_call = CANCELLED_CALL;
- fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error));
- grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
- }
- gpr_atm_rel_store(&calld->subchannel_call,
- (gpr_atm)(uintptr_t)subchannel_call);
- retry_waiting_locked(exec_ctx, calld);
- goto retry;
- }
- /* nothing to be done but wait */
- add_waiting_locked(calld, op);
- gpr_mu_unlock(&calld->mu);
- GPR_TIMER_END("cc_start_transport_stream_op", 0);
-}
-
static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand,
grpc_lb_policy *lb_policy,
grpc_connectivity_state current_state);
@@ -417,8 +163,8 @@ static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand,
&w->on_changed);
}
-static void cc_on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
- grpc_error *error) {
+static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
channel_data *chand = arg;
grpc_lb_policy *lb_policy = NULL;
grpc_lb_policy *old_lb_policy;
@@ -570,6 +316,188 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
gpr_mu_unlock(&chand->mu);
}
+/* Constructor for channel_data */
+static void cc_init_channel_elem(grpc_exec_ctx *exec_ctx,
+ grpc_channel_element *elem,
+ grpc_channel_element_args *args) {
+ channel_data *chand = elem->channel_data;
+
+ memset(chand, 0, sizeof(*chand));
+
+ GPR_ASSERT(args->is_last);
+ GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
+
+ gpr_mu_init(&chand->mu);
+ grpc_closure_init(&chand->on_resolver_result_changed,
+ on_resolver_result_changed, chand);
+ chand->owning_stack = args->channel_stack;
+
+ grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
+ "client_channel");
+ chand->interested_parties = grpc_pollset_set_create();
+}
+
+/* Destructor for channel_data */
+static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx,
+ grpc_channel_element *elem) {
+ channel_data *chand = elem->channel_data;
+
+ if (chand->resolver != NULL) {
+ grpc_resolver_shutdown(exec_ctx, chand->resolver);
+ GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
+ }
+ if (chand->lb_policy != NULL) {
+ grpc_pollset_set_del_pollset_set(exec_ctx,
+ chand->lb_policy->interested_parties,
+ chand->interested_parties);
+ GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
+ }
+ grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
+ grpc_pollset_set_destroy(chand->interested_parties);
+ gpr_mu_destroy(&chand->mu);
+}
+
+/*************************************************************************
+ * PER-CALL FUNCTIONS
+ */
+
+#define GET_CALL(call_data) \
+ ((grpc_subchannel_call *)(gpr_atm_acq_load(&(call_data)->subchannel_call)))
+
+#define CANCELLED_CALL ((grpc_subchannel_call *)1)
+
+typedef enum {
+ GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING,
+ GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL
+} subchannel_creation_phase;
+
+/** Call data. Holds a pointer to grpc_subchannel_call and the
+ associated machinery to create such a pointer.
+ Handles queueing of stream ops until a call object is ready, waiting
+ for initial metadata before trying to create a call object,
+ and handling cancellation gracefully. */
+typedef struct client_channel_call_data {
+ /** either 0 for no call, 1 for cancelled, or a pointer to a
+ grpc_subchannel_call */
+ gpr_atm subchannel_call;
+
+ gpr_mu mu;
+
+ subchannel_creation_phase creation_phase;
+ grpc_connected_subchannel *connected_subchannel;
+ grpc_polling_entity *pollent;
+
+ grpc_transport_stream_op *waiting_ops;
+ size_t waiting_ops_count;
+ size_t waiting_ops_capacity;
+
+ grpc_closure next_step;
+
+ grpc_call_stack *owning_call;
+} call_data;
+
+static void add_waiting_locked(call_data *calld, grpc_transport_stream_op *op) {
+ GPR_TIMER_BEGIN("add_waiting_locked", 0);
+ if (calld->waiting_ops_count == calld->waiting_ops_capacity) {
+ calld->waiting_ops_capacity = GPR_MAX(3, 2 * calld->waiting_ops_capacity);
+ calld->waiting_ops =
+ gpr_realloc(calld->waiting_ops,
+ calld->waiting_ops_capacity * sizeof(*calld->waiting_ops));
+ }
+ calld->waiting_ops[calld->waiting_ops_count++] = *op;
+ GPR_TIMER_END("add_waiting_locked", 0);
+}
+
+static void fail_locked(grpc_exec_ctx *exec_ctx, call_data *calld,
+ grpc_error *error) {
+ size_t i;
+ for (i = 0; i < calld->waiting_ops_count; i++) {
+ grpc_transport_stream_op_finish_with_failure(
+ exec_ctx, &calld->waiting_ops[i], GRPC_ERROR_REF(error));
+ }
+ calld->waiting_ops_count = 0;
+ GRPC_ERROR_UNREF(error);
+}
+
+typedef struct {
+ grpc_transport_stream_op *ops;
+ size_t nops;
+ grpc_subchannel_call *call;
+} retry_ops_args;
+
+static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
+ 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 retry_waiting_locked(grpc_exec_ctx *exec_ctx, call_data *calld) {
+ retry_ops_args *a = gpr_malloc(sizeof(*a));
+ a->ops = calld->waiting_ops;
+ a->nops = calld->waiting_ops_count;
+ a->call = GET_CALL(calld);
+ if (a->call == CANCELLED_CALL) {
+ gpr_free(a);
+ fail_locked(exec_ctx, calld, GRPC_ERROR_CANCELLED);
+ return;
+ }
+ calld->waiting_ops = NULL;
+ calld->waiting_ops_count = 0;
+ calld->waiting_ops_capacity = 0;
+ GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
+ grpc_exec_ctx_sched(exec_ctx, grpc_closure_create(retry_ops, a),
+ GRPC_ERROR_NONE, NULL);
+}
+
+static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
+ call_data *calld = arg;
+ gpr_mu_lock(&calld->mu);
+ GPR_ASSERT(calld->creation_phase ==
+ GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
+ calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+ if (calld->connected_subchannel == NULL) {
+ gpr_atm_no_barrier_store(&calld->subchannel_call, 1);
+ fail_locked(exec_ctx, calld, GRPC_ERROR_CREATE_REFERENCING(
+ "Failed to create subchannel", &error, 1));
+ } else if (1 == gpr_atm_acq_load(&calld->subchannel_call)) {
+ /* already cancelled before subchannel became ready */
+ fail_locked(exec_ctx, calld,
+ GRPC_ERROR_CREATE_REFERENCING(
+ "Cancelled before creating subchannel", &error, 1));
+ } else {
+ grpc_subchannel_call *subchannel_call = NULL;
+ grpc_error *new_error = grpc_connected_subchannel_create_call(
+ exec_ctx, calld->connected_subchannel, calld->pollent,
+ &subchannel_call);
+ if (new_error != GRPC_ERROR_NONE) {
+ new_error = grpc_error_add_child(new_error, error);
+ subchannel_call = CANCELLED_CALL;
+ fail_locked(exec_ctx, calld, new_error);
+ }
+ gpr_atm_rel_store(&calld->subchannel_call,
+ (gpr_atm)(uintptr_t)subchannel_call);
+ retry_waiting_locked(exec_ctx, calld);
+ }
+ gpr_mu_unlock(&calld->mu);
+ GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
+}
+
+static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
+ call_data *calld = elem->call_data;
+ grpc_subchannel_call *subchannel_call = GET_CALL(calld);
+ if (subchannel_call == NULL || subchannel_call == CANCELLED_CALL) {
+ return NULL;
+ } else {
+ return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call);
+ }
+}
+
typedef struct {
grpc_metadata_batch *initial_metadata;
uint32_t initial_metadata_flags;
@@ -579,11 +507,11 @@ typedef struct {
grpc_closure closure;
} continue_picking_args;
-static bool cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg,
- grpc_metadata_batch *initial_metadata,
- uint32_t initial_metadata_flags,
- grpc_connected_subchannel **connected_subchannel,
- grpc_closure *on_ready);
+static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ grpc_metadata_batch *initial_metadata,
+ uint32_t initial_metadata_flags,
+ grpc_connected_subchannel **connected_subchannel,
+ grpc_closure *on_ready);
static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
@@ -592,22 +520,21 @@ static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
/* cancelled, do nothing */
} else if (error != GRPC_ERROR_NONE) {
grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_REF(error), NULL);
- } else if (cc_pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
- cpa->initial_metadata_flags,
- cpa->connected_subchannel, cpa->on_ready)) {
+ } else if (pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
+ cpa->initial_metadata_flags,
+ cpa->connected_subchannel, cpa->on_ready)) {
grpc_exec_ctx_sched(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE, NULL);
}
gpr_free(cpa);
}
-static bool cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
- grpc_metadata_batch *initial_metadata,
- uint32_t initial_metadata_flags,
- grpc_connected_subchannel **connected_subchannel,
- grpc_closure *on_ready) {
- GPR_TIMER_BEGIN("cc_pick_subchannel", 0);
+static bool pick_subchannel(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ grpc_metadata_batch *initial_metadata,
+ uint32_t initial_metadata_flags,
+ grpc_connected_subchannel **connected_subchannel,
+ grpc_closure *on_ready) {
+ GPR_TIMER_BEGIN("pick_subchannel", 0);
- grpc_call_element *elem = elemp;
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
continue_picking_args *cpa;
@@ -631,19 +558,19 @@ static bool cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
}
}
gpr_mu_unlock(&chand->mu);
- GPR_TIMER_END("cc_pick_subchannel", 0);
+ GPR_TIMER_END("pick_subchannel", 0);
return true;
}
if (chand->lb_policy != NULL) {
grpc_lb_policy *lb_policy = chand->lb_policy;
int r;
- GRPC_LB_POLICY_REF(lb_policy, "cc_pick_subchannel");
+ GRPC_LB_POLICY_REF(lb_policy, "pick_subchannel");
gpr_mu_unlock(&chand->mu);
r = grpc_lb_policy_pick(exec_ctx, lb_policy, calld->pollent,
initial_metadata, initial_metadata_flags,
connected_subchannel, on_ready);
- GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "cc_pick_subchannel");
- GPR_TIMER_END("cc_pick_subchannel", 0);
+ GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "pick_subchannel");
+ GPR_TIMER_END("pick_subchannel", 0);
return r;
}
if (chand->resolver != NULL && !chand->started_resolving) {
@@ -668,18 +595,118 @@ static bool cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp,
}
gpr_mu_unlock(&chand->mu);
- GPR_TIMER_END("cc_pick_subchannel", 0);
+ GPR_TIMER_END("pick_subchannel", 0);
return false;
}
+// The logic here is fairly complicated, due to (a) the fact that we
+// need to handle the case where we receive the send op before the
+// initial metadata op, and (b) the need for efficiency, especially in
+// the streaming case.
+// TODO(ctiller): Explain this more thoroughly.
+static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_transport_stream_op *op) {
+ call_data *calld = elem->call_data;
+ GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
+ /* try to (atomically) get the call */
+ grpc_subchannel_call *call = GET_CALL(calld);
+ GPR_TIMER_BEGIN("cc_start_transport_stream_op", 0);
+ if (call == CANCELLED_CALL) {
+ grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+ GRPC_ERROR_CANCELLED);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+ return;
+ }
+ if (call != NULL) {
+ grpc_subchannel_call_process_op(exec_ctx, call, op);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+ return;
+ }
+ /* we failed; lock and figure out what to do */
+ gpr_mu_lock(&calld->mu);
+retry:
+ /* need to recheck that another thread hasn't set the call */
+ call = GET_CALL(calld);
+ if (call == CANCELLED_CALL) {
+ gpr_mu_unlock(&calld->mu);
+ grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+ GRPC_ERROR_CANCELLED);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+ return;
+ }
+ if (call != NULL) {
+ gpr_mu_unlock(&calld->mu);
+ grpc_subchannel_call_process_op(exec_ctx, call, op);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+ return;
+ }
+ /* if this is a cancellation, then we can raise our cancelled flag */
+ if (op->cancel_error != GRPC_ERROR_NONE) {
+ if (!gpr_atm_rel_cas(&calld->subchannel_call, 0,
+ (gpr_atm)(uintptr_t)CANCELLED_CALL)) {
+ goto retry;
+ } else {
+ switch (calld->creation_phase) {
+ case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
+ fail_locked(exec_ctx, calld, GRPC_ERROR_REF(op->cancel_error));
+ break;
+ case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
+ pick_subchannel(exec_ctx, elem, NULL, 0,
+ &calld->connected_subchannel, NULL);
+ break;
+ }
+ gpr_mu_unlock(&calld->mu);
+ grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+ GRPC_ERROR_CANCELLED);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+ return;
+ }
+ }
+ /* if we don't have a subchannel, try to get one */
+ if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
+ calld->connected_subchannel == NULL &&
+ op->send_initial_metadata != NULL) {
+ calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
+ grpc_closure_init(&calld->next_step, subchannel_ready, calld);
+ GRPC_CALL_STACK_REF(calld->owning_call, "pick_subchannel");
+ if (pick_subchannel(
+ exec_ctx, elem, op->send_initial_metadata,
+ op->send_initial_metadata_flags, &calld->connected_subchannel,
+ &calld->next_step)) {
+ calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+ GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
+ }
+ }
+ /* if we've got a subchannel, then let's ask it to create a call */
+ if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
+ calld->connected_subchannel != NULL) {
+ grpc_subchannel_call *subchannel_call = NULL;
+ grpc_error *error = grpc_connected_subchannel_create_call(
+ exec_ctx, calld->connected_subchannel, calld->pollent,
+ &subchannel_call);
+ if (error != GRPC_ERROR_NONE) {
+ subchannel_call = CANCELLED_CALL;
+ fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error));
+ grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error);
+ }
+ gpr_atm_rel_store(&calld->subchannel_call,
+ (gpr_atm)(uintptr_t)subchannel_call);
+ retry_waiting_locked(exec_ctx, calld);
+ goto retry;
+ }
+ /* nothing to be done but wait */
+ add_waiting_locked(calld, op);
+ gpr_mu_unlock(&calld->mu);
+ GPR_TIMER_END("cc_start_transport_stream_op", 0);
+}
+
/* Constructor for call_data */
-static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
- grpc_call_element *elem,
- grpc_call_element_args *args) {
+static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_call_element_args *args) {
call_data *calld = elem->call_data;
gpr_atm_rel_store(&calld->subchannel_call, 0);
- calld->pick_subchannel = cc_pick_subchannel;
- calld->pick_subchannel_arg = elem;
gpr_mu_init(&calld->mu);
calld->connected_subchannel = NULL;
calld->waiting_ops = NULL;
@@ -692,9 +719,10 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
}
/* Destructor for call_data */
-static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
- const grpc_call_final_info *final_info,
- void *and_free_memory) {
+static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ const grpc_call_final_info *final_info,
+ void *and_free_memory) {
call_data *calld = elem->call_data;
grpc_subchannel_call *call = GET_CALL(calld);
if (call != NULL && call != CANCELLED_CALL) {
@@ -707,47 +735,6 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
gpr_free(and_free_memory);
}
-/* Constructor for channel_data */
-static void init_channel_elem(grpc_exec_ctx *exec_ctx,
- grpc_channel_element *elem,
- grpc_channel_element_args *args) {
- channel_data *chand = elem->channel_data;
-
- memset(chand, 0, sizeof(*chand));
-
- GPR_ASSERT(args->is_last);
- GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
-
- gpr_mu_init(&chand->mu);
- grpc_closure_init(&chand->on_resolver_result_changed,
- cc_on_resolver_result_changed, chand);
- chand->owning_stack = args->channel_stack;
-
- grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE,
- "client_channel");
- chand->interested_parties = grpc_pollset_set_create();
-}
-
-/* Destructor for channel_data */
-static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
- grpc_channel_element *elem) {
- channel_data *chand = elem->channel_data;
-
- if (chand->resolver != NULL) {
- grpc_resolver_shutdown(exec_ctx, chand->resolver);
- GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
- }
- if (chand->lb_policy != NULL) {
- grpc_pollset_set_del_pollset_set(exec_ctx,
- chand->lb_policy->interested_parties,
- chand->interested_parties);
- GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
- }
- grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker);
- grpc_pollset_set_destroy(chand->interested_parties);
- gpr_mu_destroy(&chand->mu);
-}
-
static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
grpc_polling_entity *pollent) {
@@ -755,16 +742,20 @@ static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
calld->pollent = pollent;
}
+/*************************************************************************
+ * EXPORTED SYMBOLS
+ */
+
const grpc_channel_filter grpc_client_channel_filter = {
cc_start_transport_stream_op,
cc_start_transport_op,
sizeof(call_data),
- init_call_elem,
+ cc_init_call_elem,
cc_set_pollset_or_pollset_set,
- destroy_call_elem,
+ cc_destroy_call_elem,
sizeof(channel_data),
- init_channel_elem,
- destroy_channel_elem,
+ cc_init_channel_elem,
+ cc_destroy_channel_elem,
cc_get_peer,
"client-channel",
};