|
|
|
@ -72,46 +72,6 @@ |
|
|
|
|
// Used to create arena for the first call.
|
|
|
|
|
#define ESTIMATED_MDELEM_COUNT 16 |
|
|
|
|
|
|
|
|
|
/* Status data for a request can come from several sources; this
|
|
|
|
|
enumerates them all, and acts as a priority sorting for which |
|
|
|
|
status to return to the application - earlier entries override |
|
|
|
|
later ones */ |
|
|
|
|
typedef enum { |
|
|
|
|
/* Status came from the application layer overriding whatever
|
|
|
|
|
the wire says */ |
|
|
|
|
STATUS_FROM_API_OVERRIDE = 0, |
|
|
|
|
/* Status came from 'the wire' - or somewhere below the surface
|
|
|
|
|
layer */ |
|
|
|
|
STATUS_FROM_WIRE, |
|
|
|
|
/* Status was created by some internal channel stack operation: must come via
|
|
|
|
|
add_batch_error */ |
|
|
|
|
STATUS_FROM_CORE, |
|
|
|
|
/* Status was created by some surface error */ |
|
|
|
|
STATUS_FROM_SURFACE, |
|
|
|
|
/* Status came from the server sending status */ |
|
|
|
|
STATUS_FROM_SERVER_STATUS, |
|
|
|
|
STATUS_SOURCE_COUNT |
|
|
|
|
} status_source; |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
bool is_set; |
|
|
|
|
grpc_error* error; |
|
|
|
|
} received_status; |
|
|
|
|
|
|
|
|
|
static gpr_atm pack_received_status(received_status r) { |
|
|
|
|
return r.is_set ? (1 | (gpr_atm)r.error) : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static received_status unpack_received_status(gpr_atm atm) { |
|
|
|
|
if ((atm & 1) == 0) { |
|
|
|
|
return {false, GRPC_ERROR_NONE}; |
|
|
|
|
} else { |
|
|
|
|
return {true, (grpc_error*)(atm & ~static_cast<gpr_atm>(1))}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#define MAX_ERRORS_PER_BATCH 4 |
|
|
|
|
|
|
|
|
|
typedef struct batch_control { |
|
|
|
|
grpc_call* call; |
|
|
|
|
/* Share memory for cq_completion and notify_tag as they are never needed
|
|
|
|
@ -136,10 +96,7 @@ typedef struct batch_control { |
|
|
|
|
grpc_closure start_batch; |
|
|
|
|
grpc_closure finish_batch; |
|
|
|
|
gpr_refcount steps_to_complete; |
|
|
|
|
|
|
|
|
|
grpc_error* errors[MAX_ERRORS_PER_BATCH]; |
|
|
|
|
gpr_atm num_errors; |
|
|
|
|
|
|
|
|
|
grpc_error* batch_error; |
|
|
|
|
grpc_transport_stream_op_batch op; |
|
|
|
|
} batch_control; |
|
|
|
|
|
|
|
|
@ -202,9 +159,6 @@ struct grpc_call { |
|
|
|
|
// A char* indicating the peer name.
|
|
|
|
|
gpr_atm peer_string; |
|
|
|
|
|
|
|
|
|
/* Packed received call statuses from various sources */ |
|
|
|
|
gpr_atm status[STATUS_SOURCE_COUNT]; |
|
|
|
|
|
|
|
|
|
/* Call data useful used for reporting. Only valid after the call has
|
|
|
|
|
* completed */ |
|
|
|
|
grpc_call_final_info final_info; |
|
|
|
@ -237,6 +191,7 @@ struct grpc_call { |
|
|
|
|
grpc_closure receiving_initial_metadata_ready; |
|
|
|
|
grpc_closure receiving_trailing_metadata_ready; |
|
|
|
|
uint32_t test_only_last_message_flags; |
|
|
|
|
gpr_atm cancelled; |
|
|
|
|
|
|
|
|
|
grpc_closure release_call; |
|
|
|
|
|
|
|
|
@ -252,6 +207,7 @@ struct grpc_call { |
|
|
|
|
grpc_server* server; |
|
|
|
|
} server; |
|
|
|
|
} final_op; |
|
|
|
|
grpc_error* status_error; |
|
|
|
|
|
|
|
|
|
/* recv_state can contain one of the following values:
|
|
|
|
|
RECV_NONE : : no initial metadata and messages received |
|
|
|
@ -289,23 +245,15 @@ grpc_core::TraceFlag grpc_compression_trace(false, "compression"); |
|
|
|
|
|
|
|
|
|
static void execute_batch(grpc_call* call, grpc_transport_stream_op_batch* op, |
|
|
|
|
grpc_closure* start_batch_closure); |
|
|
|
|
static void cancel_with_status(grpc_call* c, status_source source, |
|
|
|
|
grpc_status_code status, |
|
|
|
|
|
|
|
|
|
static void cancel_with_status(grpc_call* c, grpc_status_code status, |
|
|
|
|
const char* description); |
|
|
|
|
static void cancel_with_error(grpc_call* c, status_source source, |
|
|
|
|
grpc_error* error); |
|
|
|
|
static void cancel_with_error(grpc_call* c, grpc_error* error); |
|
|
|
|
static void destroy_call(void* call_stack, grpc_error* error); |
|
|
|
|
static void receiving_slice_ready(void* bctlp, grpc_error* error); |
|
|
|
|
static void get_final_status( |
|
|
|
|
grpc_call* call, void (*set_value)(grpc_status_code code, void* user_data), |
|
|
|
|
void* set_value_user_data, grpc_slice* details, const char** error_string); |
|
|
|
|
static void set_status_value_directly(grpc_status_code status, void* dest); |
|
|
|
|
static void set_status_from_error(grpc_call* call, status_source source, |
|
|
|
|
grpc_error* error); |
|
|
|
|
static void set_final_status(grpc_call* call, grpc_error* error); |
|
|
|
|
static void process_data_after_md(batch_control* bctl); |
|
|
|
|
static void post_batch_completion(batch_control* bctl); |
|
|
|
|
static void add_batch_error(batch_control* bctl, grpc_error* error, |
|
|
|
|
bool has_cancelled); |
|
|
|
|
|
|
|
|
|
static void add_init_error(grpc_error** composite, grpc_error* new_err) { |
|
|
|
|
if (new_err == GRPC_ERROR_NONE) return; |
|
|
|
@ -356,6 +304,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, |
|
|
|
|
gpr_arena_alloc(arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) + |
|
|
|
|
channel_stack->call_stack_size)); |
|
|
|
|
gpr_ref_init(&call->ext_ref, 1); |
|
|
|
|
gpr_atm_no_barrier_store(&call->cancelled, 0); |
|
|
|
|
call->arena = arena; |
|
|
|
|
grpc_call_combiner_init(&call->call_combiner); |
|
|
|
|
*out_call = call; |
|
|
|
@ -465,10 +414,10 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, |
|
|
|
|
gpr_mu_unlock(&pc->child_list_mu); |
|
|
|
|
} |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); |
|
|
|
|
cancel_with_error(call, GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
if (immediately_cancel) { |
|
|
|
|
cancel_with_error(call, STATUS_FROM_API_OVERRIDE, GRPC_ERROR_CANCELLED); |
|
|
|
|
cancel_with_error(call, GRPC_ERROR_CANCELLED); |
|
|
|
|
} |
|
|
|
|
if (args->cq != nullptr) { |
|
|
|
|
GPR_ASSERT(args->pollset_set_alternative == nullptr && |
|
|
|
@ -570,16 +519,13 @@ static void destroy_call(void* call, grpc_error* error) { |
|
|
|
|
GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
get_final_status(c, set_status_value_directly, &c->final_info.final_status, |
|
|
|
|
nullptr, &(c->final_info.error_string)); |
|
|
|
|
grpc_error_get_status(c->status_error, c->send_deadline, |
|
|
|
|
&c->final_info.final_status, nullptr, nullptr, |
|
|
|
|
&(c->final_info.error_string)); |
|
|
|
|
GRPC_ERROR_UNREF(c->status_error); |
|
|
|
|
c->final_info.stats.latency = |
|
|
|
|
gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { |
|
|
|
|
GRPC_ERROR_UNREF( |
|
|
|
|
unpack_received_status(gpr_atm_acq_load(&c->status[i])).error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c), &c->final_info, |
|
|
|
|
GRPC_CLOSURE_INIT(&c->release_call, release_call, c, |
|
|
|
|
grpc_schedule_on_exec_ctx)); |
|
|
|
@ -617,7 +563,7 @@ void grpc_call_unref(grpc_call* c) { |
|
|
|
|
bool cancel = gpr_atm_acq_load(&c->any_ops_sent_atm) != 0 && |
|
|
|
|
gpr_atm_acq_load(&c->received_final_op_atm) == 0; |
|
|
|
|
if (cancel) { |
|
|
|
|
cancel_with_error(c, STATUS_FROM_API_OVERRIDE, GRPC_ERROR_CANCELLED); |
|
|
|
|
cancel_with_error(c, GRPC_ERROR_CANCELLED); |
|
|
|
|
} else { |
|
|
|
|
// Unset the call combiner cancellation closure. This has the
|
|
|
|
|
// effect of scheduling the previously set cancellation closure, if
|
|
|
|
@ -635,8 +581,7 @@ grpc_call_error grpc_call_cancel(grpc_call* call, void* reserved) { |
|
|
|
|
GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved)); |
|
|
|
|
GPR_ASSERT(!reserved); |
|
|
|
|
grpc_core::ExecCtx exec_ctx; |
|
|
|
|
cancel_with_error(call, STATUS_FROM_API_OVERRIDE, GRPC_ERROR_CANCELLED); |
|
|
|
|
|
|
|
|
|
cancel_with_error(call, GRPC_ERROR_CANCELLED); |
|
|
|
|
return GRPC_CALL_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -690,8 +635,7 @@ grpc_call_error grpc_call_cancel_with_status(grpc_call* c, |
|
|
|
|
"c=%p, status=%d, description=%s, reserved=%p)", |
|
|
|
|
4, (c, (int)status, description, reserved)); |
|
|
|
|
GPR_ASSERT(reserved == nullptr); |
|
|
|
|
cancel_with_status(c, STATUS_FROM_API_OVERRIDE, status, description); |
|
|
|
|
|
|
|
|
|
cancel_with_status(c, status, description); |
|
|
|
|
return GRPC_CALL_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -711,15 +655,17 @@ static void done_termination(void* arg, grpc_error* error) { |
|
|
|
|
gpr_free(state); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void cancel_with_error(grpc_call* c, status_source source, |
|
|
|
|
grpc_error* error) { |
|
|
|
|
static void cancel_with_error(grpc_call* c, grpc_error* error) { |
|
|
|
|
if (!gpr_atm_rel_cas(&c->cancelled, 0, 1)) { |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
GRPC_CALL_INTERNAL_REF(c, "termination"); |
|
|
|
|
// Inform the call combiner of the cancellation, so that it can cancel
|
|
|
|
|
// any in-flight asynchronous actions that may be holding the call
|
|
|
|
|
// combiner. This ensures that the cancel_stream batch can be sent
|
|
|
|
|
// down the filter stack in a timely manner.
|
|
|
|
|
grpc_call_combiner_cancel(&c->call_combiner, GRPC_ERROR_REF(error)); |
|
|
|
|
set_status_from_error(c, source, GRPC_ERROR_REF(error)); |
|
|
|
|
cancel_state* state = static_cast<cancel_state*>(gpr_malloc(sizeof(*state))); |
|
|
|
|
state->call = c; |
|
|
|
|
GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state, |
|
|
|
@ -742,90 +688,45 @@ static grpc_error* error_from_status(grpc_status_code status, |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, status); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void cancel_with_status(grpc_call* c, status_source source, |
|
|
|
|
grpc_status_code status, |
|
|
|
|
static void cancel_with_status(grpc_call* c, grpc_status_code status, |
|
|
|
|
const char* description) { |
|
|
|
|
cancel_with_error(c, source, error_from_status(status, description)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* FINAL STATUS CODE MANIPULATION |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static bool get_final_status_from( |
|
|
|
|
grpc_call* call, grpc_error* error, bool allow_ok_status, |
|
|
|
|
void (*set_value)(grpc_status_code code, void* user_data), |
|
|
|
|
void* set_value_user_data, grpc_slice* details, const char** error_string) { |
|
|
|
|
grpc_status_code code; |
|
|
|
|
grpc_slice slice = grpc_empty_slice(); |
|
|
|
|
grpc_error_get_status(error, call->send_deadline, &code, &slice, nullptr, |
|
|
|
|
error_string); |
|
|
|
|
if (code == GRPC_STATUS_OK && !allow_ok_status) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
set_value(code, set_value_user_data); |
|
|
|
|
if (details != nullptr) { |
|
|
|
|
*details = grpc_slice_ref_internal(slice); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
cancel_with_error(c, error_from_status(status, description)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void get_final_status( |
|
|
|
|
grpc_call* call, void (*set_value)(grpc_status_code code, void* user_data), |
|
|
|
|
void* set_value_user_data, grpc_slice* details, const char** error_string) { |
|
|
|
|
int i; |
|
|
|
|
received_status status[STATUS_SOURCE_COUNT]; |
|
|
|
|
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { |
|
|
|
|
status[i] = unpack_received_status(gpr_atm_acq_load(&call->status[i])); |
|
|
|
|
} |
|
|
|
|
static void set_final_status(grpc_call* call, grpc_error* error) { |
|
|
|
|
if (grpc_call_error_trace.enabled()) { |
|
|
|
|
gpr_log(GPR_INFO, "get_final_status %s", call->is_client ? "CLI" : "SVR"); |
|
|
|
|
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { |
|
|
|
|
if (status[i].is_set) { |
|
|
|
|
gpr_log(GPR_INFO, " %d: %s", i, grpc_error_string(status[i].error)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* first search through ignoring "OK" statuses: if something went wrong,
|
|
|
|
|
* ensure we report it */ |
|
|
|
|
for (int allow_ok_status = 0; allow_ok_status < 2; allow_ok_status++) { |
|
|
|
|
/* search for the best status we can present: ideally the error we use has a
|
|
|
|
|
clearly defined grpc-status, and we'll prefer that. */ |
|
|
|
|
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { |
|
|
|
|
if (status[i].is_set && |
|
|
|
|
grpc_error_has_clear_grpc_status(status[i].error)) { |
|
|
|
|
if (get_final_status_from(call, status[i].error, allow_ok_status != 0, |
|
|
|
|
set_value, set_value_user_data, details, |
|
|
|
|
error_string)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* If no clearly defined status exists, search for 'anything' */ |
|
|
|
|
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { |
|
|
|
|
if (status[i].is_set) { |
|
|
|
|
if (get_final_status_from(call, status[i].error, allow_ok_status != 0, |
|
|
|
|
set_value, set_value_user_data, details, |
|
|
|
|
error_string)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
gpr_log(GPR_DEBUG, "set_final_status %s", call->is_client ? "CLI" : "SVR"); |
|
|
|
|
gpr_log(GPR_DEBUG, "%s", grpc_error_string(error)); |
|
|
|
|
} |
|
|
|
|
if (call->is_client) { |
|
|
|
|
grpc_error_get_status(error, call->send_deadline, |
|
|
|
|
call->final_op.client.status, |
|
|
|
|
call->final_op.client.status_details, nullptr, |
|
|
|
|
call->final_op.client.error_string); |
|
|
|
|
// explicitly take a ref
|
|
|
|
|
grpc_slice_ref_internal(*call->final_op.client.status_details); |
|
|
|
|
call->status_error = error; |
|
|
|
|
grpc_core::channelz::ChannelNode* channelz_channel = |
|
|
|
|
grpc_channel_get_channelz_node(call->channel); |
|
|
|
|
if (channelz_channel != nullptr) { |
|
|
|
|
if (*call->final_op.client.status != GRPC_STATUS_OK) { |
|
|
|
|
channelz_channel->RecordCallFailed(); |
|
|
|
|
} else { |
|
|
|
|
channelz_channel->RecordCallSucceeded(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* If nothing exists, set some default */ |
|
|
|
|
if (call->is_client) { |
|
|
|
|
set_value(GRPC_STATUS_UNKNOWN, set_value_user_data); |
|
|
|
|
} else { |
|
|
|
|
set_value(GRPC_STATUS_OK, set_value_user_data); |
|
|
|
|
*call->final_op.server.cancelled = |
|
|
|
|
error != GRPC_ERROR_NONE || call->status_error != GRPC_ERROR_NONE; |
|
|
|
|
grpc_core::channelz::ServerNode* channelz_server = |
|
|
|
|
grpc_server_get_channelz_node(call->final_op.server.server); |
|
|
|
|
if (channelz_server != nullptr) { |
|
|
|
|
if (*call->final_op.server.cancelled) { |
|
|
|
|
channelz_server->RecordCallFailed(); |
|
|
|
|
} else { |
|
|
|
|
channelz_server->RecordCallSucceeded(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void set_status_from_error(grpc_call* call, status_source source, |
|
|
|
|
grpc_error* error) { |
|
|
|
|
if (!gpr_atm_rel_cas(&call->status[source], |
|
|
|
|
pack_received_status({false, GRPC_ERROR_NONE}), |
|
|
|
|
pack_received_status({true, error}))) { |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1044,6 +945,7 @@ static grpc_stream_compression_algorithm decode_stream_compression( |
|
|
|
|
static void publish_app_metadata(grpc_call* call, grpc_metadata_batch* b, |
|
|
|
|
int is_trailing) { |
|
|
|
|
if (b->list.count == 0) return; |
|
|
|
|
if (!call->is_client && is_trailing) return; |
|
|
|
|
if (is_trailing && call->buffered_metadata[1] == nullptr) return; |
|
|
|
|
GPR_TIMER_SCOPE("publish_app_metadata", 0); |
|
|
|
|
grpc_metadata_array* dest; |
|
|
|
@ -1097,9 +999,12 @@ static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) { |
|
|
|
|
publish_app_metadata(call, b, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void recv_trailing_filter(void* args, grpc_metadata_batch* b) { |
|
|
|
|
static void recv_trailing_filter(void* args, grpc_metadata_batch* b, |
|
|
|
|
grpc_error* batch_error) { |
|
|
|
|
grpc_call* call = static_cast<grpc_call*>(args); |
|
|
|
|
if (b->idx.named.grpc_status != nullptr) { |
|
|
|
|
if (batch_error != GRPC_ERROR_NONE) { |
|
|
|
|
set_final_status(call, batch_error); |
|
|
|
|
} else if (b->idx.named.grpc_status != nullptr) { |
|
|
|
|
grpc_status_code status_code = |
|
|
|
|
grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md); |
|
|
|
|
grpc_error* error = GRPC_ERROR_NONE; |
|
|
|
@ -1117,8 +1022,18 @@ static void recv_trailing_filter(void* args, grpc_metadata_batch* b) { |
|
|
|
|
error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, |
|
|
|
|
grpc_empty_slice()); |
|
|
|
|
} |
|
|
|
|
set_status_from_error(call, STATUS_FROM_WIRE, error); |
|
|
|
|
set_final_status(call, GRPC_ERROR_REF(error)); |
|
|
|
|
grpc_metadata_batch_remove(b, b->idx.named.grpc_status); |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
} else if (!call->is_client) { |
|
|
|
|
set_final_status(call, GRPC_ERROR_NONE); |
|
|
|
|
} else { |
|
|
|
|
gpr_log(GPR_DEBUG, |
|
|
|
|
"Received trailing metadata with no error and no status"); |
|
|
|
|
set_final_status( |
|
|
|
|
call, grpc_error_set_int( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("No status received"), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNKNOWN)); |
|
|
|
|
} |
|
|
|
|
publish_app_metadata(call, b, true); |
|
|
|
|
} |
|
|
|
@ -1133,14 +1048,6 @@ grpc_call_stack* grpc_call_get_call_stack(grpc_call* call) { |
|
|
|
|
* BATCH API IMPLEMENTATION |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void set_status_value_directly(grpc_status_code status, void* dest) { |
|
|
|
|
*static_cast<grpc_status_code*>(dest) = status; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void set_cancelled_value(grpc_status_code status, void* dest) { |
|
|
|
|
*static_cast<int*>(dest) = (status != GRPC_STATUS_OK); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool are_write_flags_valid(uint32_t flags) { |
|
|
|
|
/* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ |
|
|
|
|
const uint32_t allowed_write_positions = |
|
|
|
@ -1208,31 +1115,15 @@ static void finish_batch_completion(void* user_data, |
|
|
|
|
GRPC_CALL_INTERNAL_UNREF(call, "completion"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_error* consolidate_batch_errors(batch_control* bctl) { |
|
|
|
|
size_t n = static_cast<size_t>(gpr_atm_acq_load(&bctl->num_errors)); |
|
|
|
|
if (n == 0) { |
|
|
|
|
return GRPC_ERROR_NONE; |
|
|
|
|
} else if (n == 1) { |
|
|
|
|
/* Skip creating a composite error in the case that only one error was
|
|
|
|
|
logged */ |
|
|
|
|
grpc_error* e = bctl->errors[0]; |
|
|
|
|
bctl->errors[0] = nullptr; |
|
|
|
|
return e; |
|
|
|
|
} else { |
|
|
|
|
grpc_error* error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
|
|
|
|
"Call batch failed", bctl->errors, n); |
|
|
|
|
for (size_t i = 0; i < n; i++) { |
|
|
|
|
GRPC_ERROR_UNREF(bctl->errors[i]); |
|
|
|
|
bctl->errors[i] = nullptr; |
|
|
|
|
} |
|
|
|
|
return error; |
|
|
|
|
} |
|
|
|
|
static void reset_batch_errors(batch_control* bctl) { |
|
|
|
|
GRPC_ERROR_UNREF(bctl->batch_error); |
|
|
|
|
bctl->batch_error = GRPC_ERROR_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void post_batch_completion(batch_control* bctl) { |
|
|
|
|
grpc_call* next_child_call; |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
grpc_error* error = consolidate_batch_errors(bctl); |
|
|
|
|
grpc_error* error = GRPC_ERROR_REF(bctl->batch_error); |
|
|
|
|
|
|
|
|
|
if (bctl->op.send_initial_metadata) { |
|
|
|
|
grpc_metadata_batch_destroy( |
|
|
|
@ -1258,8 +1149,7 @@ static void post_batch_completion(batch_control* bctl) { |
|
|
|
|
next_child_call = child->child->sibling_next; |
|
|
|
|
if (child->cancellation_is_inherited) { |
|
|
|
|
GRPC_CALL_INTERNAL_REF(child, "propagate_cancel"); |
|
|
|
|
cancel_with_error(child, STATUS_FROM_API_OVERRIDE, |
|
|
|
|
GRPC_ERROR_CANCELLED); |
|
|
|
|
cancel_with_error(child, GRPC_ERROR_CANCELLED); |
|
|
|
|
GRPC_CALL_INTERNAL_UNREF(child, "propagate_cancel"); |
|
|
|
|
} |
|
|
|
|
child = next_child_call; |
|
|
|
@ -1267,33 +1157,6 @@ static void post_batch_completion(batch_control* bctl) { |
|
|
|
|
} |
|
|
|
|
gpr_mu_unlock(&pc->child_list_mu); |
|
|
|
|
} |
|
|
|
|
if (call->is_client) { |
|
|
|
|
get_final_status(call, set_status_value_directly, |
|
|
|
|
call->final_op.client.status, |
|
|
|
|
call->final_op.client.status_details, |
|
|
|
|
call->final_op.client.error_string); |
|
|
|
|
grpc_core::channelz::ChannelNode* channelz_channel = |
|
|
|
|
grpc_channel_get_channelz_node(call->channel); |
|
|
|
|
if (channelz_channel != nullptr) { |
|
|
|
|
if (*call->final_op.client.status != GRPC_STATUS_OK) { |
|
|
|
|
channelz_channel->RecordCallFailed(); |
|
|
|
|
} else { |
|
|
|
|
channelz_channel->RecordCallSucceeded(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
get_final_status(call, set_cancelled_value, |
|
|
|
|
call->final_op.server.cancelled, nullptr, nullptr); |
|
|
|
|
grpc_core::channelz::ServerNode* channelz_server = |
|
|
|
|
grpc_server_get_channelz_node(call->final_op.server.server); |
|
|
|
|
if (channelz_server != nullptr) { |
|
|
|
|
if (*call->final_op.server.cancelled) { |
|
|
|
|
channelz_server->RecordCallFailed(); |
|
|
|
|
} else { |
|
|
|
|
channelz_server->RecordCallSucceeded(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
error = GRPC_ERROR_NONE; |
|
|
|
|
} |
|
|
|
@ -1302,9 +1165,10 @@ static void post_batch_completion(batch_control* bctl) { |
|
|
|
|
grpc_byte_buffer_destroy(*call->receiving_buffer); |
|
|
|
|
*call->receiving_buffer = nullptr; |
|
|
|
|
} |
|
|
|
|
reset_batch_errors(bctl); |
|
|
|
|
|
|
|
|
|
if (bctl->completion_data.notify_tag.is_closure) { |
|
|
|
|
/* unrefs bctl->error */ |
|
|
|
|
/* unrefs error */ |
|
|
|
|
bctl->call = nullptr; |
|
|
|
|
/* This closure may be meant to be run within some combiner. Since we aren't
|
|
|
|
|
* running in any combiner here, we need to use GRPC_CLOSURE_SCHED instead |
|
|
|
@ -1314,7 +1178,7 @@ static void post_batch_completion(batch_control* bctl) { |
|
|
|
|
error); |
|
|
|
|
GRPC_CALL_INTERNAL_UNREF(call, "completion"); |
|
|
|
|
} else { |
|
|
|
|
/* unrefs bctl->error */ |
|
|
|
|
/* unrefs error */ |
|
|
|
|
grpc_cq_end_op(bctl->call->cq, bctl->completion_data.notify_tag.tag, error, |
|
|
|
|
finish_batch_completion, bctl, |
|
|
|
|
&bctl->completion_data.cq_completion); |
|
|
|
@ -1423,8 +1287,10 @@ static void receiving_stream_ready(void* bctlp, grpc_error* error) { |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
call->receiving_stream.reset(); |
|
|
|
|
add_batch_error(bctl, GRPC_ERROR_REF(error), true); |
|
|
|
|
cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); |
|
|
|
|
if (bctl->batch_error == GRPC_ERROR_NONE) { |
|
|
|
|
bctl->batch_error = GRPC_ERROR_REF(error); |
|
|
|
|
} |
|
|
|
|
cancel_with_error(call, GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
/* If recv_state is RECV_NONE, we will save the batch_control
|
|
|
|
|
* object with rel_cas, and will not use it after the cas. Its corresponding |
|
|
|
@ -1442,7 +1308,7 @@ static void receiving_stream_ready_in_call_combiner(void* bctlp, |
|
|
|
|
grpc_error* error) { |
|
|
|
|
batch_control* bctl = static_cast<batch_control*>(bctlp); |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "call_recv_message_ready"); |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_message_ready"); |
|
|
|
|
receiving_stream_ready(bctlp, error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1460,8 +1326,7 @@ static void validate_filtered_metadata(batch_control* bctl) { |
|
|
|
|
call->incoming_stream_compression_algorithm, |
|
|
|
|
call->incoming_message_compression_algorithm); |
|
|
|
|
gpr_log(GPR_ERROR, "%s", error_msg); |
|
|
|
|
cancel_with_status(call, STATUS_FROM_SURFACE, GRPC_STATUS_INTERNAL, |
|
|
|
|
error_msg); |
|
|
|
|
cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg); |
|
|
|
|
gpr_free(error_msg); |
|
|
|
|
} else if ( |
|
|
|
|
grpc_compression_algorithm_from_message_stream_compression_algorithm( |
|
|
|
@ -1473,8 +1338,7 @@ static void validate_filtered_metadata(batch_control* bctl) { |
|
|
|
|
"compression (%d).", |
|
|
|
|
call->incoming_stream_compression_algorithm, |
|
|
|
|
call->incoming_message_compression_algorithm); |
|
|
|
|
cancel_with_status(call, STATUS_FROM_SURFACE, GRPC_STATUS_INTERNAL, |
|
|
|
|
error_msg); |
|
|
|
|
cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg); |
|
|
|
|
gpr_free(error_msg); |
|
|
|
|
} else { |
|
|
|
|
char* error_msg = nullptr; |
|
|
|
@ -1484,8 +1348,7 @@ static void validate_filtered_metadata(batch_control* bctl) { |
|
|
|
|
gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.", |
|
|
|
|
compression_algorithm); |
|
|
|
|
gpr_log(GPR_ERROR, "%s", error_msg); |
|
|
|
|
cancel_with_status(call, STATUS_FROM_SURFACE, GRPC_STATUS_UNIMPLEMENTED, |
|
|
|
|
error_msg); |
|
|
|
|
cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg); |
|
|
|
|
} else if (grpc_compression_options_is_algorithm_enabled( |
|
|
|
|
&compression_options, compression_algorithm) == 0) { |
|
|
|
|
/* check if algorithm is supported by current channel config */ |
|
|
|
@ -1494,8 +1357,7 @@ static void validate_filtered_metadata(batch_control* bctl) { |
|
|
|
|
gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.", |
|
|
|
|
algo_name); |
|
|
|
|
gpr_log(GPR_ERROR, "%s", error_msg); |
|
|
|
|
cancel_with_status(call, STATUS_FROM_SURFACE, GRPC_STATUS_UNIMPLEMENTED, |
|
|
|
|
error_msg); |
|
|
|
|
cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg); |
|
|
|
|
} |
|
|
|
|
gpr_free(error_msg); |
|
|
|
|
|
|
|
|
@ -1513,24 +1375,12 @@ static void validate_filtered_metadata(batch_control* bctl) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void add_batch_error(batch_control* bctl, grpc_error* error, |
|
|
|
|
bool has_cancelled) { |
|
|
|
|
if (error == GRPC_ERROR_NONE) return; |
|
|
|
|
int idx = static_cast<int>(gpr_atm_full_fetch_add(&bctl->num_errors, 1)); |
|
|
|
|
if (idx == 0 && !has_cancelled) { |
|
|
|
|
cancel_with_error(bctl->call, STATUS_FROM_CORE, GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
bctl->errors[idx] = error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) { |
|
|
|
|
batch_control* bctl = static_cast<batch_control*>(bctlp); |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
|
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, |
|
|
|
|
"call_recv_initial_metadata_ready"); |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_initial_metadata_ready"); |
|
|
|
|
|
|
|
|
|
add_batch_error(bctl, GRPC_ERROR_REF(error), false); |
|
|
|
|
if (error == GRPC_ERROR_NONE) { |
|
|
|
|
grpc_metadata_batch* md = |
|
|
|
|
&call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; |
|
|
|
@ -1543,6 +1393,11 @@ static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) { |
|
|
|
|
if (md->deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) { |
|
|
|
|
call->send_deadline = md->deadline; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (bctl->batch_error == GRPC_ERROR_NONE) { |
|
|
|
|
bctl->batch_error = GRPC_ERROR_REF(error); |
|
|
|
|
} |
|
|
|
|
cancel_with_error(call, GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
grpc_closure* saved_rsr_closure = nullptr; |
|
|
|
@ -1579,20 +1434,23 @@ static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) { |
|
|
|
|
static void receiving_trailing_metadata_ready(void* bctlp, grpc_error* error) { |
|
|
|
|
batch_control* bctl = static_cast<batch_control*>(bctlp); |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, |
|
|
|
|
"call_recv_trailing_metadata_ready"); |
|
|
|
|
add_batch_error(bctl, GRPC_ERROR_REF(error), false); |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_trailing_metadata_ready"); |
|
|
|
|
grpc_metadata_batch* md = |
|
|
|
|
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; |
|
|
|
|
recv_trailing_filter(call, md); |
|
|
|
|
recv_trailing_filter(call, md, GRPC_ERROR_REF(error)); |
|
|
|
|
finish_batch_step(bctl); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void finish_batch(void* bctlp, grpc_error* error) { |
|
|
|
|
batch_control* bctl = static_cast<batch_control*>(bctlp); |
|
|
|
|
grpc_call* call = bctl->call; |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "call_on_complete"); |
|
|
|
|
add_batch_error(bctl, GRPC_ERROR_REF(error), false); |
|
|
|
|
GRPC_CALL_COMBINER_STOP(&call->call_combiner, "on_complete"); |
|
|
|
|
if (bctl->batch_error == GRPC_ERROR_NONE) { |
|
|
|
|
bctl->batch_error = GRPC_ERROR_REF(error); |
|
|
|
|
} |
|
|
|
|
if (error != GRPC_ERROR_NONE) { |
|
|
|
|
cancel_with_error(call, GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
finish_batch_step(bctl); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1794,28 +1652,32 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, |
|
|
|
|
call->send_extra_metadata_count = 1; |
|
|
|
|
call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( |
|
|
|
|
call->channel, op->data.send_status_from_server.status); |
|
|
|
|
{ |
|
|
|
|
grpc_error* override_error = GRPC_ERROR_NONE; |
|
|
|
|
if (op->data.send_status_from_server.status != GRPC_STATUS_OK) { |
|
|
|
|
override_error = |
|
|
|
|
error_from_status(op->data.send_status_from_server.status, |
|
|
|
|
"Returned non-ok status"); |
|
|
|
|
} |
|
|
|
|
grpc_error* status_error = |
|
|
|
|
op->data.send_status_from_server.status == GRPC_STATUS_OK |
|
|
|
|
? GRPC_ERROR_NONE |
|
|
|
|
: grpc_error_set_int( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"Server returned error"), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, |
|
|
|
|
static_cast<intptr_t>( |
|
|
|
|
op->data.send_status_from_server.status)); |
|
|
|
|
if (op->data.send_status_from_server.status_details != nullptr) { |
|
|
|
|
call->send_extra_metadata[1].md = grpc_mdelem_from_slices( |
|
|
|
|
GRPC_MDSTR_GRPC_MESSAGE, |
|
|
|
|
grpc_slice_ref_internal( |
|
|
|
|
*op->data.send_status_from_server.status_details)); |
|
|
|
|
call->send_extra_metadata_count++; |
|
|
|
|
if (status_error != GRPC_ERROR_NONE) { |
|
|
|
|
char* msg = grpc_slice_to_c_string( |
|
|
|
|
GRPC_MDVALUE(call->send_extra_metadata[1].md)); |
|
|
|
|
override_error = |
|
|
|
|
grpc_error_set_str(override_error, GRPC_ERROR_STR_GRPC_MESSAGE, |
|
|
|
|
status_error = |
|
|
|
|
grpc_error_set_str(status_error, GRPC_ERROR_STR_GRPC_MESSAGE, |
|
|
|
|
grpc_slice_from_copied_string(msg)); |
|
|
|
|
gpr_free(msg); |
|
|
|
|
} |
|
|
|
|
set_status_from_error(call, STATUS_FROM_API_OVERRIDE, override_error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
call->status_error = status_error; |
|
|
|
|
if (!prepare_application_metadata( |
|
|
|
|
call, |
|
|
|
|
static_cast<int>( |
|
|
|
|