|  | @@ -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,44 +316,225 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    gpr_mu_unlock(&chand->mu);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef struct {
 | 
	
		
			
				|  |  | -  grpc_metadata_batch *initial_metadata;
 | 
	
		
			
				|  |  | -  uint32_t initial_metadata_flags;
 | 
	
		
			
				|  |  | -  grpc_connected_subchannel **connected_subchannel;
 | 
	
		
			
				|  |  | -  grpc_closure *on_ready;
 | 
	
		
			
				|  |  | -  grpc_call_element *elem;
 | 
	
		
			
				|  |  | -  grpc_closure closure;
 | 
	
		
			
				|  |  | -} continue_picking_args;
 | 
	
		
			
				|  |  | +/* 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;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -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);
 | 
	
		
			
				|  |  | +  memset(chand, 0, sizeof(*chand));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  | -                             grpc_error *error) {
 | 
	
		
			
				|  |  | -  continue_picking_args *cpa = arg;
 | 
	
		
			
				|  |  | -  if (cpa->connected_subchannel == NULL) {
 | 
	
		
			
				|  |  | +  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;
 | 
	
		
			
				|  |  | +  grpc_connected_subchannel **connected_subchannel;
 | 
	
		
			
				|  |  | +  grpc_closure *on_ready;
 | 
	
		
			
				|  |  | +  grpc_call_element *elem;
 | 
	
		
			
				|  |  | +  grpc_closure closure;
 | 
	
		
			
				|  |  | +} continue_picking_args;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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) {
 | 
	
		
			
				|  |  | +  continue_picking_args *cpa = arg;
 | 
	
		
			
				|  |  | +  if (cpa->connected_subchannel == NULL) {
 | 
	
		
			
				|  |  |      /* 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",
 | 
	
		
			
				|  |  |  };
 |