|
|
@ -1016,13 +1016,11 @@ static void create_subchannel_call_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void subchannel_ready_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
// Invoked when a pick is completed, on both success or failure.
|
|
|
|
grpc_call_element *elem, |
|
|
|
static void pick_done_locked(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, |
|
|
|
grpc_error *error) { |
|
|
|
grpc_error *error) { |
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent, |
|
|
|
|
|
|
|
chand->interested_parties); |
|
|
|
|
|
|
|
if (calld->connected_subchannel == NULL) { |
|
|
|
if (calld->connected_subchannel == NULL) { |
|
|
|
// Failed to create subchannel.
|
|
|
|
// Failed to create subchannel.
|
|
|
|
GRPC_ERROR_UNREF(calld->error); |
|
|
|
GRPC_ERROR_UNREF(calld->error); |
|
|
@ -1044,12 +1042,116 @@ static void subchannel_ready_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Return true if subchannel is available immediately (in which case
|
|
|
|
// A wrapper around pick_done_locked() that is used in cases where
|
|
|
|
subchannel_ready_locked() should not be called), or false otherwise (in |
|
|
|
// either (a) the pick was deferred pending a resolver result or (b) the
|
|
|
|
which case subchannel_ready_locked() should be called when the subchannel |
|
|
|
// pick was done asynchronously. Removes the call's polling entity from
|
|
|
|
is available). */ |
|
|
|
// chand->interested_parties before invoking pick_done_locked().
|
|
|
|
static bool pick_subchannel_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
static void async_pick_done_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_call_element *elem); |
|
|
|
grpc_call_element *elem, grpc_error *error) { |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent, |
|
|
|
|
|
|
|
chand->interested_parties); |
|
|
|
|
|
|
|
pick_done_locked(exec_ctx, elem, error); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Note: This runs under the client_channel combiner, but will NOT be
|
|
|
|
|
|
|
|
// holding the call combiner.
|
|
|
|
|
|
|
|
static void pick_callback_cancel_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
|
|
|
grpc_error *error) { |
|
|
|
|
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
if (calld->lb_policy != NULL) { |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p", |
|
|
|
|
|
|
|
chand, calld, calld->lb_policy); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
grpc_lb_policy_cancel_pick_locked(exec_ctx, calld->lb_policy, |
|
|
|
|
|
|
|
&calld->connected_subchannel, |
|
|
|
|
|
|
|
GRPC_ERROR_REF(error)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_callback_cancel"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Callback invoked by grpc_lb_policy_pick_locked() for async picks.
|
|
|
|
|
|
|
|
// Unrefs the LB policy and invokes async_pick_done_locked().
|
|
|
|
|
|
|
|
static void pick_callback_done_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
|
|
|
grpc_error *error) { |
|
|
|
|
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously", |
|
|
|
|
|
|
|
chand, calld); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GPR_ASSERT(calld->lb_policy != NULL); |
|
|
|
|
|
|
|
GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); |
|
|
|
|
|
|
|
calld->lb_policy = NULL; |
|
|
|
|
|
|
|
async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Takes a ref to chand->lb_policy and calls grpc_lb_policy_pick_locked().
|
|
|
|
|
|
|
|
// If the pick was completed synchronously, unrefs the LB policy and
|
|
|
|
|
|
|
|
// returns true.
|
|
|
|
|
|
|
|
static bool pick_callback_start_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
|
|
|
grpc_call_element *elem) { |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p", |
|
|
|
|
|
|
|
chand, calld, chand->lb_policy); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
apply_service_config_to_call_locked(exec_ctx, elem); |
|
|
|
|
|
|
|
// If the application explicitly set wait_for_ready, use that.
|
|
|
|
|
|
|
|
// Otherwise, if the service config specified a value for this
|
|
|
|
|
|
|
|
// method, use that.
|
|
|
|
|
|
|
|
uint32_t initial_metadata_flags = |
|
|
|
|
|
|
|
calld->initial_metadata_batch->payload->send_initial_metadata |
|
|
|
|
|
|
|
.send_initial_metadata_flags; |
|
|
|
|
|
|
|
const bool wait_for_ready_set_from_api = |
|
|
|
|
|
|
|
initial_metadata_flags & |
|
|
|
|
|
|
|
GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; |
|
|
|
|
|
|
|
const bool wait_for_ready_set_from_service_config = |
|
|
|
|
|
|
|
calld->method_params != NULL && |
|
|
|
|
|
|
|
calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET; |
|
|
|
|
|
|
|
if (!wait_for_ready_set_from_api && wait_for_ready_set_from_service_config) { |
|
|
|
|
|
|
|
if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) { |
|
|
|
|
|
|
|
initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
const grpc_lb_policy_pick_args inputs = { |
|
|
|
|
|
|
|
calld->initial_metadata_batch->payload->send_initial_metadata |
|
|
|
|
|
|
|
.send_initial_metadata, |
|
|
|
|
|
|
|
initial_metadata_flags, &calld->lb_token_mdelem}; |
|
|
|
|
|
|
|
// Keep a ref to the LB policy in calld while the pick is pending.
|
|
|
|
|
|
|
|
GRPC_LB_POLICY_REF(chand->lb_policy, "pick_subchannel"); |
|
|
|
|
|
|
|
calld->lb_policy = chand->lb_policy; |
|
|
|
|
|
|
|
GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem, |
|
|
|
|
|
|
|
grpc_combiner_scheduler(chand->combiner)); |
|
|
|
|
|
|
|
const bool pick_done = grpc_lb_policy_pick_locked( |
|
|
|
|
|
|
|
exec_ctx, chand->lb_policy, &inputs, &calld->connected_subchannel, |
|
|
|
|
|
|
|
calld->subchannel_call_context, NULL, &calld->lb_pick_closure); |
|
|
|
|
|
|
|
if (pick_done) { |
|
|
|
|
|
|
|
/* synchronous grpc_lb_policy_pick call. Unref the LB policy. */ |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously", |
|
|
|
|
|
|
|
chand, calld); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); |
|
|
|
|
|
|
|
calld->lb_policy = NULL; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel"); |
|
|
|
|
|
|
|
grpc_call_combiner_set_notify_on_cancel( |
|
|
|
|
|
|
|
exec_ctx, calld->call_combiner, |
|
|
|
|
|
|
|
GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure, |
|
|
|
|
|
|
|
pick_callback_cancel_locked, elem, |
|
|
|
|
|
|
|
grpc_combiner_scheduler(chand->combiner))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return pick_done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
typedef struct { |
|
|
|
grpc_call_element *elem; |
|
|
|
grpc_call_element *elem; |
|
|
@ -1069,17 +1171,17 @@ static void pick_after_resolver_result_cancel_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
gpr_free(args); |
|
|
|
gpr_free(args); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
args->finished = true; |
|
|
|
|
|
|
|
grpc_call_element *elem = args->elem; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
// If we don't yet have a resolver result, then a closure for
|
|
|
|
// If we don't yet have a resolver result, then a closure for
|
|
|
|
// pick_after_resolver_result_done_locked() will have been added to
|
|
|
|
// pick_after_resolver_result_done_locked() will have been added to
|
|
|
|
// chand->waiting_for_resolver_result_closures, and it may not be invoked
|
|
|
|
// chand->waiting_for_resolver_result_closures, and it may not be invoked
|
|
|
|
// until after this call has been destroyed. We mark the operation as
|
|
|
|
// until after this call has been destroyed. We mark the operation as
|
|
|
|
// finished, so that when pick_after_resolver_result_done_locked()
|
|
|
|
// finished, so that when pick_after_resolver_result_done_locked()
|
|
|
|
// is called, it will be a no-op. We also immediately invoke
|
|
|
|
// is called, it will be a no-op. We also immediately invoke
|
|
|
|
// subchannel_ready_locked() to propagate the error back to the caller.
|
|
|
|
// async_pick_done_locked() to propagate the error back to the caller.
|
|
|
|
|
|
|
|
args->finished = true; |
|
|
|
|
|
|
|
grpc_call_element *elem = args->elem; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
"chand=%p calld=%p: cancelling pick waiting for resolver result", |
|
|
|
"chand=%p calld=%p: cancelling pick waiting for resolver result", |
|
|
@ -1087,12 +1189,12 @@ static void pick_after_resolver_result_cancel_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
} |
|
|
|
} |
|
|
|
// Note: Although we are not in the call combiner here, we are
|
|
|
|
// Note: Although we are not in the call combiner here, we are
|
|
|
|
// basically stealing the call combiner from the pending pick, so
|
|
|
|
// basically stealing the call combiner from the pending pick, so
|
|
|
|
// it's safe to call subchannel_ready_locked() here -- we are
|
|
|
|
// it's safe to call async_pick_done_locked() here -- we are
|
|
|
|
// essentially calling it here instead of calling it in
|
|
|
|
// essentially calling it here instead of calling it in
|
|
|
|
// pick_after_resolver_result_done_locked().
|
|
|
|
// pick_after_resolver_result_done_locked().
|
|
|
|
subchannel_ready_locked(exec_ctx, elem, |
|
|
|
async_pick_done_locked(exec_ctx, elem, |
|
|
|
GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
|
|
|
GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
|
|
|
"Pick cancelled", &error, 1)); |
|
|
|
"Pick cancelled", &error, 1)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void pick_after_resolver_result_done_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
static void pick_after_resolver_result_done_locked(grpc_exec_ctx *exec_ctx, |
|
|
@ -1117,14 +1219,19 @@ static void pick_after_resolver_result_done_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data", |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data", |
|
|
|
chand, calld); |
|
|
|
chand, calld); |
|
|
|
} |
|
|
|
} |
|
|
|
subchannel_ready_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); |
|
|
|
async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick", |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick", |
|
|
|
chand, calld); |
|
|
|
chand, calld); |
|
|
|
} |
|
|
|
} |
|
|
|
if (pick_subchannel_locked(exec_ctx, elem)) { |
|
|
|
if (pick_callback_start_locked(exec_ctx, elem)) { |
|
|
|
subchannel_ready_locked(exec_ctx, elem, GRPC_ERROR_NONE); |
|
|
|
// Even if the LB policy returns a result synchronously, we have
|
|
|
|
|
|
|
|
// already added our polling entity to chand->interested_parties
|
|
|
|
|
|
|
|
// in order to wait for the resolver result, so we need to
|
|
|
|
|
|
|
|
// remove it here. Therefore, we call async_pick_done_locked()
|
|
|
|
|
|
|
|
// instead of pick_done_locked().
|
|
|
|
|
|
|
|
async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -1152,154 +1259,38 @@ static void pick_after_resolver_result_start_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
grpc_combiner_scheduler(chand->combiner))); |
|
|
|
grpc_combiner_scheduler(chand->combiner))); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Note: This runs under the client_channel combiner, but will NOT be
|
|
|
|
static void start_pick_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
// holding the call combiner.
|
|
|
|
grpc_error *ignored) { |
|
|
|
static void pick_callback_cancel_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
|
|
|
grpc_error *error) { |
|
|
|
|
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
if (error != GRPC_ERROR_NONE && calld->lb_policy != NULL) { |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p", |
|
|
|
|
|
|
|
chand, calld, calld->lb_policy); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
grpc_lb_policy_cancel_pick_locked(exec_ctx, calld->lb_policy, |
|
|
|
|
|
|
|
&calld->connected_subchannel, |
|
|
|
|
|
|
|
GRPC_ERROR_REF(error)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_callback_cancel"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Callback invoked by grpc_lb_policy_pick_locked() for async picks.
|
|
|
|
|
|
|
|
// Unrefs the LB policy and invokes subchannel_ready_locked().
|
|
|
|
|
|
|
|
static void pick_callback_done_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
|
|
|
grpc_error *error) { |
|
|
|
|
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously", |
|
|
|
|
|
|
|
chand, calld); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GPR_ASSERT(calld->lb_policy != NULL); |
|
|
|
|
|
|
|
GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); |
|
|
|
|
|
|
|
calld->lb_policy = NULL; |
|
|
|
|
|
|
|
subchannel_ready_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Takes a ref to chand->lb_policy and calls grpc_lb_policy_pick_locked().
|
|
|
|
|
|
|
|
// If the pick was completed synchronously, unrefs the LB policy and
|
|
|
|
|
|
|
|
// returns true.
|
|
|
|
|
|
|
|
static bool pick_callback_start_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
|
|
|
grpc_call_element *elem, |
|
|
|
|
|
|
|
const grpc_lb_policy_pick_args *inputs) { |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
GPR_ASSERT(calld->connected_subchannel == NULL); |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
if (chand->lb_policy != NULL) { |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p", |
|
|
|
// We already have an LB policy, so ask it for a pick.
|
|
|
|
chand, calld, chand->lb_policy); |
|
|
|
if (pick_callback_start_locked(exec_ctx, elem)) { |
|
|
|
} |
|
|
|
// Pick completed synchronously.
|
|
|
|
// Keep a ref to the LB policy in calld while the pick is pending.
|
|
|
|
pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); |
|
|
|
GRPC_LB_POLICY_REF(chand->lb_policy, "pick_subchannel"); |
|
|
|
return; |
|
|
|
calld->lb_policy = chand->lb_policy; |
|
|
|
|
|
|
|
GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem, |
|
|
|
|
|
|
|
grpc_combiner_scheduler(chand->combiner)); |
|
|
|
|
|
|
|
const bool pick_done = grpc_lb_policy_pick_locked( |
|
|
|
|
|
|
|
exec_ctx, chand->lb_policy, inputs, &calld->connected_subchannel, |
|
|
|
|
|
|
|
calld->subchannel_call_context, NULL, &calld->lb_pick_closure); |
|
|
|
|
|
|
|
if (pick_done) { |
|
|
|
|
|
|
|
/* synchronous grpc_lb_policy_pick call. Unref the LB policy. */ |
|
|
|
|
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
|
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously", |
|
|
|
|
|
|
|
chand, calld); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); |
|
|
|
|
|
|
|
calld->lb_policy = NULL; |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel"); |
|
|
|
// We do not yet have an LB policy, so wait for a resolver result.
|
|
|
|
grpc_call_combiner_set_notify_on_cancel( |
|
|
|
if (chand->resolver == NULL) { |
|
|
|
exec_ctx, calld->call_combiner, |
|
|
|
pick_done_locked(exec_ctx, elem, |
|
|
|
GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure, |
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); |
|
|
|
pick_callback_cancel_locked, elem, |
|
|
|
return; |
|
|
|
grpc_combiner_scheduler(chand->combiner))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return pick_done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool pick_subchannel_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
|
|
|
grpc_call_element *elem) { |
|
|
|
|
|
|
|
GPR_TIMER_BEGIN("pick_subchannel", 0); |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
bool pick_done = false; |
|
|
|
|
|
|
|
if (chand->lb_policy != NULL) { |
|
|
|
|
|
|
|
apply_service_config_to_call_locked(exec_ctx, elem); |
|
|
|
|
|
|
|
// If the application explicitly set wait_for_ready, use that.
|
|
|
|
|
|
|
|
// Otherwise, if the service config specified a value for this
|
|
|
|
|
|
|
|
// method, use that.
|
|
|
|
|
|
|
|
uint32_t initial_metadata_flags = |
|
|
|
|
|
|
|
calld->initial_metadata_batch->payload->send_initial_metadata |
|
|
|
|
|
|
|
.send_initial_metadata_flags; |
|
|
|
|
|
|
|
const bool wait_for_ready_set_from_api = |
|
|
|
|
|
|
|
initial_metadata_flags & |
|
|
|
|
|
|
|
GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; |
|
|
|
|
|
|
|
const bool wait_for_ready_set_from_service_config = |
|
|
|
|
|
|
|
calld->method_params != NULL && |
|
|
|
|
|
|
|
calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET; |
|
|
|
|
|
|
|
if (!wait_for_ready_set_from_api && |
|
|
|
|
|
|
|
wait_for_ready_set_from_service_config) { |
|
|
|
|
|
|
|
if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) { |
|
|
|
|
|
|
|
initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
const grpc_lb_policy_pick_args inputs = { |
|
|
|
|
|
|
|
calld->initial_metadata_batch->payload->send_initial_metadata |
|
|
|
|
|
|
|
.send_initial_metadata, |
|
|
|
|
|
|
|
initial_metadata_flags, &calld->lb_token_mdelem}; |
|
|
|
|
|
|
|
pick_done = pick_callback_start_locked(exec_ctx, elem, &inputs); |
|
|
|
|
|
|
|
} else if (chand->resolver != NULL) { |
|
|
|
|
|
|
|
if (!chand->started_resolving) { |
|
|
|
if (!chand->started_resolving) { |
|
|
|
start_resolving_locked(exec_ctx, chand); |
|
|
|
start_resolving_locked(exec_ctx, chand); |
|
|
|
} |
|
|
|
} |
|
|
|
pick_after_resolver_result_start_locked(exec_ctx, elem); |
|
|
|
pick_after_resolver_result_start_locked(exec_ctx, elem); |
|
|
|
} else { |
|
|
|
|
|
|
|
subchannel_ready_locked( |
|
|
|
|
|
|
|
exec_ctx, elem, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
GPR_TIMER_END("pick_subchannel", 0); |
|
|
|
|
|
|
|
return pick_done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void start_pick_locked(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
|
|
|
grpc_error *error_ignored) { |
|
|
|
|
|
|
|
GPR_TIMER_BEGIN("start_pick_locked", 0); |
|
|
|
|
|
|
|
grpc_call_element *elem = (grpc_call_element *)arg; |
|
|
|
|
|
|
|
call_data *calld = (call_data *)elem->call_data; |
|
|
|
|
|
|
|
channel_data *chand = (channel_data *)elem->channel_data; |
|
|
|
|
|
|
|
GPR_ASSERT(calld->connected_subchannel == NULL); |
|
|
|
|
|
|
|
if (pick_subchannel_locked(exec_ctx, elem)) { |
|
|
|
|
|
|
|
// Pick was returned synchronously.
|
|
|
|
|
|
|
|
if (calld->connected_subchannel == NULL) { |
|
|
|
|
|
|
|
GRPC_ERROR_UNREF(calld->error); |
|
|
|
|
|
|
|
calld->error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
|
|
|
"Call dropped by load balancing policy"); |
|
|
|
|
|
|
|
waiting_for_pick_batches_fail(exec_ctx, elem, |
|
|
|
|
|
|
|
GRPC_ERROR_REF(calld->error)); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Create subchannel call.
|
|
|
|
|
|
|
|
create_subchannel_call_locked(exec_ctx, elem, GRPC_ERROR_NONE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// Pick will be done asynchronously. Add the call's polling entity to
|
|
|
|
|
|
|
|
// the channel's interested_parties, so that I/O for the resolver
|
|
|
|
|
|
|
|
// and LB policy can be done under it.
|
|
|
|
|
|
|
|
grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent, |
|
|
|
|
|
|
|
chand->interested_parties); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
GPR_TIMER_END("start_pick_locked", 0); |
|
|
|
// We need to wait for either a resolver result or for an async result
|
|
|
|
|
|
|
|
// from the LB policy. Add the polling entity from call_data to the
|
|
|
|
|
|
|
|
// channel_data's interested_parties, so that the I/O of the LB policy
|
|
|
|
|
|
|
|
// and resolver can be done under it. The polling entity will be
|
|
|
|
|
|
|
|
// removed in async_pick_done_locked().
|
|
|
|
|
|
|
|
grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent, |
|
|
|
|
|
|
|
chand->interested_parties); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
|
|
|
static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
|
|
@ -1394,7 +1385,8 @@ static void cc_start_transport_stream_op_batch( |
|
|
|
// combiner to start a pick.
|
|
|
|
// combiner to start a pick.
|
|
|
|
if (batch->send_initial_metadata) { |
|
|
|
if (batch->send_initial_metadata) { |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
if (GRPC_TRACER_ON(grpc_client_channel_trace)) { |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering combiner", chand, calld); |
|
|
|
gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering client_channel combiner", |
|
|
|
|
|
|
|
chand, calld); |
|
|
|
} |
|
|
|
} |
|
|
|
GRPC_CLOSURE_SCHED( |
|
|
|
GRPC_CLOSURE_SCHED( |
|
|
|
exec_ctx, |
|
|
|
exec_ctx, |
|
|
|