static void continue_receiving_slices(grpc_exec_ctx *exec_ctx, batch_control *bctl) { grpc_call *call = bctl->call; for (;;) { size_t remaining = call->receiving_stream->length - (*call->receiving_buffer)->data.raw.slice_buffer.length; if (remaining == 0) { call->receiving_message = 0; grpc_byte_stream_destroy(call->receiving_stream); call->receiving_stream = NULL; if (gpr_unref(&bctl->steps_to_complete)) { post_batch_completion(exec_ctx, bctl); } return; } if (grpc_byte_stream_next(exec_ctx, call->receiving_stream, &call->receiving_slice, remaining, &call->receiving_slice_ready)) { gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, call->receiving_slice); } else { return; } } }
static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, int success) { size_t i; int ii; grpc_call *c = call; GPR_TIMER_BEGIN("destroy_call", 0); for (i = 0; i < 2; i++) { grpc_metadata_batch_destroy( &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); } if (c->receiving_stream != NULL) { grpc_byte_stream_destroy(c->receiving_stream); } grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c)); GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->channel, "call"); gpr_mu_destroy(&c->mu); for (i = 0; i < STATUS_SOURCE_COUNT; i++) { if (c->status[i].details) { GRPC_MDSTR_UNREF(c->status[i].details); } } for (ii = 0; ii < c->send_extra_metadata_count; ii++) { GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md); } for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { if (c->context[i].destroy) { c->context[i].destroy(c->context[i].value); } } if (c->cq) { GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); } gpr_free(c); GPR_TIMER_END("destroy_call", 0); }
void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx, grpc_chttp2_data_parser *parser) { grpc_byte_stream *bs; if (parser->parsing_frame) { grpc_chttp2_incoming_byte_stream_finished(exec_ctx, parser->parsing_frame); } while ( (bs = grpc_chttp2_incoming_frame_queue_pop(&parser->incoming_frames))) { grpc_byte_stream_destroy(bs); } }
/* called from application code */ static void on_md_processing_done( void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, const grpc_metadata *response_md, size_t num_response_md, grpc_status_code status, const char *error_details) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; /* TODO(jboeuf): Implement support for response_md. */ if (response_md != NULL && num_response_md > 0) { gpr_log(GPR_INFO, "response_md in auth metadata processing not supported for now. " "Ignoring..."); } if (status == GRPC_STATUS_OK) { calld->consumed_md = consumed_md; calld->num_consumed_md = num_consumed_md; grpc_metadata_batch_filter(calld->recv_initial_metadata, remove_consumed_md, elem); grpc_metadata_array_destroy(&calld->md); calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1); } else { gpr_slice message; grpc_transport_stream_op close_op; memset(&close_op, 0, sizeof(close_op)); grpc_metadata_array_destroy(&calld->md); error_details = error_details != NULL ? error_details : "Authentication metadata processing failed."; message = gpr_slice_from_copied_string(error_details); calld->transport_op.send_initial_metadata = NULL; if (calld->transport_op.send_message != NULL) { grpc_byte_stream_destroy(calld->transport_op.send_message); calld->transport_op.send_message = NULL; } calld->transport_op.send_trailing_metadata = NULL; grpc_transport_stream_op_add_close(&close_op, status, &message); grpc_call_next_op(&exec_ctx, elem, &close_op); calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 0); } grpc_exec_ctx_finish(&exec_ctx); }
void grpc_transport_stream_op_batch_finish_with_failure( grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *batch, grpc_error *error) { if (batch->send_message) { grpc_byte_stream_destroy(exec_ctx, batch->payload->send_message.send_message); } if (batch->recv_message) { GRPC_CLOSURE_SCHED(exec_ctx, batch->payload->recv_message.recv_message_ready, GRPC_ERROR_REF(error)); } if (batch->recv_initial_metadata) { GRPC_CLOSURE_SCHED( exec_ctx, batch->payload->recv_initial_metadata.recv_initial_metadata_ready, GRPC_ERROR_REF(error)); } GRPC_CLOSURE_SCHED(exec_ctx, batch->on_complete, error); if (batch->cancel_stream) { GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); } }
static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, int success) { batch_control *bctl = bctlp; grpc_call *call = bctl->call; if (call->receiving_stream == NULL) { *call->receiving_buffer = NULL; if (gpr_unref(&bctl->steps_to_complete)) { post_batch_completion(exec_ctx, bctl); } } else if (call->receiving_stream->length > grpc_channel_get_max_message_length(call->channel)) { cancel_with_status(exec_ctx, call, GRPC_STATUS_INTERNAL, "Max message size exceeded"); grpc_byte_stream_destroy(call->receiving_stream); call->receiving_stream = NULL; *call->receiving_buffer = NULL; if (gpr_unref(&bctl->steps_to_complete)) { post_batch_completion(exec_ctx, bctl); } } else { call->test_only_last_message_flags = call->receiving_stream->flags; if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && (call->compression_algorithm > GRPC_COMPRESS_NONE)) { *call->receiving_buffer = grpc_raw_compressed_byte_buffer_create( NULL, 0, call->compression_algorithm); } else { *call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0); } grpc_closure_init(&call->receiving_slice_ready, receiving_slice_ready, bctl); continue_receiving_slices(exec_ctx, bctl); /* early out */ return; } }
static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, const grpc_op *ops, size_t nops, void *notify_tag, int is_notify_tag_closure) { grpc_transport_stream_op stream_op; size_t i; const grpc_op *op; batch_control *bctl; int num_completion_callbacks_needed = 1; grpc_call_error error = GRPC_CALL_OK; GPR_TIMER_BEGIN("grpc_call_start_batch", 0); GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag); memset(&stream_op, 0, sizeof(stream_op)); /* TODO(ctiller): this feels like it could be made lock-free */ gpr_mu_lock(&call->mu); bctl = allocate_batch_control(call); memset(bctl, 0, sizeof(*bctl)); bctl->call = call; bctl->notify_tag = notify_tag; bctl->is_notify_tag_closure = (gpr_uint8)(is_notify_tag_closure != 0); if (nops == 0) { GRPC_CALL_INTERNAL_REF(call, "completion"); bctl->success = 1; if (!is_notify_tag_closure) { grpc_cq_begin_op(call->cq); } gpr_mu_unlock(&call->mu); post_batch_completion(exec_ctx, bctl); return GRPC_CALL_OK; } /* rewrite batch ops into a transport op */ for (i = 0; i < nops; i++) { op = &ops[i]; if (op->reserved != NULL) { error = GRPC_CALL_ERROR; goto done_with_error; } switch (op->op) { case GRPC_OP_SEND_INITIAL_METADATA: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (call->sent_initial_metadata) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } if (op->data.send_initial_metadata.count > INT_MAX) { error = GRPC_CALL_ERROR_INVALID_METADATA; goto done_with_error; } bctl->send_initial_metadata = 1; call->sent_initial_metadata = 1; if (!prepare_application_metadata( call, (int)op->data.send_initial_metadata.count, op->data.send_initial_metadata.metadata, 0, call->is_client)) { error = GRPC_CALL_ERROR_INVALID_METADATA; goto done_with_error; } /* TODO(ctiller): just make these the same variable? */ call->metadata_batch[0][0].deadline = call->send_deadline; stream_op.send_initial_metadata = &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; break; case GRPC_OP_SEND_MESSAGE: if (!are_write_flags_valid(op->flags)) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (op->data.send_message == NULL) { error = GRPC_CALL_ERROR_INVALID_MESSAGE; goto done_with_error; } if (call->sending_message) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } bctl->send_message = 1; call->sending_message = 1; grpc_slice_buffer_stream_init( &call->sending_stream, &op->data.send_message->data.raw.slice_buffer, op->flags); stream_op.send_message = &call->sending_stream.base; break; case GRPC_OP_SEND_CLOSE_FROM_CLIENT: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (!call->is_client) { error = GRPC_CALL_ERROR_NOT_ON_SERVER; goto done_with_error; } if (call->sent_final_op) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } bctl->send_final_op = 1; call->sent_final_op = 1; stream_op.send_trailing_metadata = &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; break; case GRPC_OP_SEND_STATUS_FROM_SERVER: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (call->is_client) { error = GRPC_CALL_ERROR_NOT_ON_CLIENT; goto done_with_error; } if (call->sent_final_op) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } if (op->data.send_status_from_server.trailing_metadata_count > INT_MAX) { error = GRPC_CALL_ERROR_INVALID_METADATA; goto done_with_error; } bctl->send_final_op = 1; call->sent_final_op = 1; 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); if (op->data.send_status_from_server.status_details != NULL) { call->send_extra_metadata[1].md = grpc_mdelem_from_metadata_strings( call->metadata_context, GRPC_MDSTR_REF(grpc_channel_get_message_string(call->channel)), grpc_mdstr_from_string( call->metadata_context, op->data.send_status_from_server.status_details)); call->send_extra_metadata_count++; set_status_details( call, STATUS_FROM_API_OVERRIDE, GRPC_MDSTR_REF(call->send_extra_metadata[1].md->value)); } set_status_code(call, STATUS_FROM_API_OVERRIDE, (gpr_uint32)op->data.send_status_from_server.status); if (!prepare_application_metadata( call, (int)op->data.send_status_from_server.trailing_metadata_count, op->data.send_status_from_server.trailing_metadata, 1, 1)) { error = GRPC_CALL_ERROR_INVALID_METADATA; goto done_with_error; } stream_op.send_trailing_metadata = &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; break; case GRPC_OP_RECV_INITIAL_METADATA: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (call->received_initial_metadata) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } call->received_initial_metadata = 1; call->buffered_metadata[0] = op->data.recv_initial_metadata; bctl->recv_initial_metadata = 1; stream_op.recv_initial_metadata = &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; break; case GRPC_OP_RECV_MESSAGE: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (call->receiving_message) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; } call->receiving_message = 1; bctl->recv_message = 1; call->receiving_buffer = op->data.recv_message; stream_op.recv_message = &call->receiving_stream; grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready, bctl); stream_op.recv_message_ready = &call->receiving_stream_ready; num_completion_callbacks_needed++; break; case GRPC_OP_RECV_STATUS_ON_CLIENT: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (!call->is_client) { error = GRPC_CALL_ERROR_NOT_ON_SERVER; goto done_with_error; } if (call->received_final_op) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } call->received_final_op = 1; call->buffered_metadata[1] = op->data.recv_status_on_client.trailing_metadata; call->final_op.client.status = op->data.recv_status_on_client.status; call->final_op.client.status_details = op->data.recv_status_on_client.status_details; call->final_op.client.status_details_capacity = op->data.recv_status_on_client.status_details_capacity; bctl->recv_final_op = 1; stream_op.recv_trailing_metadata = &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; break; case GRPC_OP_RECV_CLOSE_ON_SERVER: /* Flag validation: currently allow no flags */ if (op->flags != 0) { error = GRPC_CALL_ERROR_INVALID_FLAGS; goto done_with_error; } if (call->is_client) { error = GRPC_CALL_ERROR_NOT_ON_CLIENT; goto done_with_error; } if (call->received_final_op) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } call->received_final_op = 1; call->final_op.server.cancelled = op->data.recv_close_on_server.cancelled; bctl->recv_final_op = 1; stream_op.recv_trailing_metadata = &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; break; } } GRPC_CALL_INTERNAL_REF(call, "completion"); if (!is_notify_tag_closure) { grpc_cq_begin_op(call->cq); } gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); stream_op.context = call->context; grpc_closure_init(&bctl->finish_batch, finish_batch, bctl); stream_op.on_complete = &bctl->finish_batch; gpr_mu_unlock(&call->mu); execute_op(exec_ctx, call, &stream_op); done: GPR_TIMER_END("grpc_call_start_batch", 0); return error; done_with_error: /* reverse any mutations that occured */ if (bctl->send_initial_metadata) { call->sent_initial_metadata = 0; grpc_metadata_batch_clear(&call->metadata_batch[0][0]); } if (bctl->send_message) { call->sending_message = 0; grpc_byte_stream_destroy(&call->sending_stream.base); } if (bctl->send_final_op) { call->sent_final_op = 0; grpc_metadata_batch_clear(&call->metadata_batch[0][1]); } if (bctl->recv_initial_metadata) { call->received_initial_metadata = 0; } if (bctl->recv_message) { call->receiving_message = 0; } if (bctl->recv_final_op) { call->received_final_op = 0; } gpr_mu_unlock(&call->mu); goto done; }
static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, grpc_stream *gs, grpc_transport_stream_op_batch *op) { INPROC_LOG(GPR_DEBUG, "perform_stream_op %p %p %p", gt, gs, op); inproc_stream *s = (inproc_stream *)gs; gpr_mu *mu = &s->t->mu->mu; // save aside in case s gets closed gpr_mu_lock(mu); if (GRPC_TRACER_ON(grpc_inproc_trace)) { if (op->send_initial_metadata) { log_metadata(op->payload->send_initial_metadata.send_initial_metadata, s->t->is_client, true); } if (op->send_trailing_metadata) { log_metadata(op->payload->send_trailing_metadata.send_trailing_metadata, s->t->is_client, false); } } grpc_error *error = GRPC_ERROR_NONE; grpc_closure *on_complete = op->on_complete; if (on_complete == NULL) { on_complete = &do_nothing_closure; } if (op->cancel_stream) { // Call cancel_stream_locked without ref'ing the cancel_error because // this function is responsible to make sure that that field gets unref'ed cancel_stream_locked(exec_ctx, s, op->payload->cancel_stream.cancel_error); // this op can complete without an error } else if (s->cancel_self_error != GRPC_ERROR_NONE) { // already self-canceled so still give it an error error = GRPC_ERROR_REF(s->cancel_self_error); } else { INPROC_LOG(GPR_DEBUG, "perform_stream_op %p%s%s%s%s%s%s", s, op->send_initial_metadata ? " send_initial_metadata" : "", op->send_message ? " send_message" : "", op->send_trailing_metadata ? " send_trailing_metadata" : "", op->recv_initial_metadata ? " recv_initial_metadata" : "", op->recv_message ? " recv_message" : "", op->recv_trailing_metadata ? " recv_trailing_metadata" : ""); } bool needs_close = false; if (error == GRPC_ERROR_NONE && (op->send_initial_metadata || op->send_message || op->send_trailing_metadata)) { inproc_stream *other = s->other_side; if (s->t->is_closed) { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Endpoint already shutdown"); } if (error == GRPC_ERROR_NONE && op->send_initial_metadata) { grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_initial_md : &other->to_read_initial_md; uint32_t *destflags = (other == NULL) ? &s->write_buffer_initial_md_flags : &other->to_read_initial_md_flags; bool *destfilled = (other == NULL) ? &s->write_buffer_initial_md_filled : &other->to_read_initial_md_filled; if (*destfilled || s->initial_md_sent) { // The buffer is already in use; that's an error! INPROC_LOG(GPR_DEBUG, "Extra initial metadata %p", s); error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra initial metadata"); } else { if (!other->closed) { fill_in_metadata( exec_ctx, s, op->payload->send_initial_metadata.send_initial_metadata, op->payload->send_initial_metadata.send_initial_metadata_flags, dest, destflags, destfilled); } if (s->t->is_client) { gpr_timespec *dl = (other == NULL) ? &s->write_buffer_deadline : &other->deadline; *dl = gpr_time_min(*dl, op->payload->send_initial_metadata .send_initial_metadata->deadline); s->initial_md_sent = true; } } } if (error == GRPC_ERROR_NONE && op->send_message) { size_t remaining = op->payload->send_message.send_message->length; grpc_slice_buffer *dest = slice_buffer_list_append( (other == NULL) ? &s->write_buffer_message : &other->to_read_message); do { grpc_slice message_slice; grpc_closure unused; GPR_ASSERT(grpc_byte_stream_next(exec_ctx, op->payload->send_message.send_message, SIZE_MAX, &unused)); error = grpc_byte_stream_pull( exec_ctx, op->payload->send_message.send_message, &message_slice); if (error != GRPC_ERROR_NONE) { cancel_stream_locked(exec_ctx, s, GRPC_ERROR_REF(error)); break; } GPR_ASSERT(error == GRPC_ERROR_NONE); remaining -= GRPC_SLICE_LENGTH(message_slice); grpc_slice_buffer_add(dest, message_slice); } while (remaining != 0); grpc_byte_stream_destroy(exec_ctx, op->payload->send_message.send_message); } if (error == GRPC_ERROR_NONE && op->send_trailing_metadata) { grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md : &other->to_read_trailing_md; bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled : &other->to_read_trailing_md_filled; if (*destfilled || s->trailing_md_sent) { // The buffer is already in use; that's an error! INPROC_LOG(GPR_DEBUG, "Extra trailing metadata %p", s); error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra trailing metadata"); } else { if (!other->closed) { fill_in_metadata( exec_ctx, s, op->payload->send_trailing_metadata.send_trailing_metadata, 0, dest, NULL, destfilled); } s->trailing_md_sent = true; if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { INPROC_LOG(GPR_DEBUG, "perform_stream_op %p scheduling trailing-md-on-complete", s); GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, GRPC_ERROR_NONE); s->recv_trailing_md_op = NULL; needs_close = true; } } } if (other != NULL && other->reads_needed) { if (!other->read_closure_scheduled) { GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, error); other->read_closure_scheduled = true; } other->reads_needed = false; } } if (error == GRPC_ERROR_NONE && (op->recv_initial_metadata || op->recv_message || op->recv_trailing_metadata)) { // If there are any reads, mark it so that the read closure will react to // them if (op->recv_initial_metadata) { s->recv_initial_md_op = op; } if (op->recv_message) { s->recv_message_op = op; } if (op->recv_trailing_metadata) { s->recv_trailing_md_op = op; } // We want to initiate the closure if: // 1. There is initial metadata and something ready to take that // 2. There is a message and something ready to take it // 3. There is trailing metadata, even if nothing specifically wants // that because that can shut down the message as well if ((s->to_read_initial_md_filled && op->recv_initial_metadata) || ((!slice_buffer_list_empty(&s->to_read_message) || s->trailing_md_recvd) && op->recv_message) || (s->to_read_trailing_md_filled)) { if (!s->read_closure_scheduled) { GRPC_CLOSURE_SCHED(exec_ctx, &s->read_closure, GRPC_ERROR_NONE); s->read_closure_scheduled = true; } } else { s->reads_needed = true; } } else { if (error != GRPC_ERROR_NONE) { // Schedule op's read closures that we didn't push to read state machine if (op->recv_initial_metadata) { INPROC_LOG( GPR_DEBUG, "perform_stream_op error %p scheduling initial-metadata-ready %p", s, error); GRPC_CLOSURE_SCHED( exec_ctx, op->payload->recv_initial_metadata.recv_initial_metadata_ready, GRPC_ERROR_REF(error)); } if (op->recv_message) { INPROC_LOG( GPR_DEBUG, "perform_stream_op error %p scheduling recv message-ready %p", s, error); GRPC_CLOSURE_SCHED(exec_ctx, op->payload->recv_message.recv_message_ready, GRPC_ERROR_REF(error)); } } INPROC_LOG(GPR_DEBUG, "perform_stream_op %p scheduling on_complete %p", s, error); GRPC_CLOSURE_SCHED(exec_ctx, on_complete, GRPC_ERROR_REF(error)); } if (needs_close) { close_other_side_locked(exec_ctx, s, "perform_stream_op:other_side"); close_stream_locked(exec_ctx, s); } gpr_mu_unlock(mu); GRPC_ERROR_UNREF(error); }