static void unlock_check_channel_callbacks(grpc_chttp2_transport *t) { if (t->channel_callback.executing) { return; } if (t->global.goaway_state != GRPC_CHTTP2_ERROR_STATE_NONE) { if (t->global.goaway_state == GRPC_CHTTP2_ERROR_STATE_SEEN && t->global.error_state != GRPC_CHTTP2_ERROR_STATE_NOTIFIED) { notify_goaways_args *a = gpr_malloc(sizeof(*a)); a->t = t; a->error = t->global.goaway_error; a->text = t->global.goaway_text; t->global.goaway_state = GRPC_CHTTP2_ERROR_STATE_NOTIFIED; t->channel_callback.executing = 1; grpc_iomgr_closure_init(&a->closure, notify_goaways, a); REF_TRANSPORT(t, "notify_goaways"); grpc_chttp2_schedule_closure(&t->global, &a->closure, 1); return; } else if (t->global.goaway_state != GRPC_CHTTP2_ERROR_STATE_NOTIFIED) { return; } } if (t->global.error_state == GRPC_CHTTP2_ERROR_STATE_SEEN) { t->global.error_state = GRPC_CHTTP2_ERROR_STATE_NOTIFIED; t->channel_callback.executing = 1; REF_TRANSPORT(t, "notify_closed"); grpc_chttp2_schedule_closure(&t->global, &t->channel_callback.notify_closed, 1); } }
void grpc_chttp2_transport_start_reading(grpc_transport *transport, gpr_slice *slices, size_t nslices) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ gpr_slice_buffer_addn(&t->read_buffer, slices, nslices); recv_data(t, 1); }
/* tcp read callback */ static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, int success) { size_t i; int keep_reading = 0; grpc_chttp2_transport *t = tp; lock(t); i = 0; GPR_ASSERT(!t->parsing_active); if (!t->closed) { t->parsing_active = 1; /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); grpc_chttp2_prepare_to_read(&t->global, &t->parsing); gpr_mu_unlock(&t->mu); for (; i < t->read_buffer.count && grpc_chttp2_perform_read(exec_ctx, &t->parsing, t->read_buffer.slices[i]); i++) ; gpr_mu_lock(&t->mu); if (i != t->read_buffer.count) { drop_connection(exec_ctx, t); } /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); t->global.concurrent_stream_count = (gpr_uint32)grpc_chttp2_stream_map_size(&t->parsing_stream_map); if (t->parsing.initial_window_update != 0) { grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, update_global_window, t); t->parsing.initial_window_update = 0; } /* handle higher level things */ grpc_chttp2_publish_reads(exec_ctx, &t->global, &t->parsing); t->parsing_active = 0; } if (!success || i != t->read_buffer.count) { drop_connection(exec_ctx, t); read_error_locked(exec_ctx, t); } else if (!t->closed) { keep_reading = 1; REF_TRANSPORT(t, "keep_reading"); prevent_endpoint_shutdown(t); } gpr_slice_buffer_reset_and_unref(&t->read_buffer); unlock(exec_ctx, t); if (keep_reading) { grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->recv_data); allow_endpoint_shutdown_unlocked(exec_ctx, t); UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); } else { UNREF_TRANSPORT(exec_ctx, t, "recv_data"); } }
static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { unlock_check_read_write_state(exec_ctx, t); if (!t->writing_active && !t->closed && grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { t->writing_active = 1; REF_TRANSPORT(t, "writing"); grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1); prevent_endpoint_shutdown(t); } gpr_mu_unlock(&t->mu); }
static int init_stream(grpc_transport *gt, grpc_stream *gs, const void *server_data, grpc_transport_stream_op *initial_op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; memset(s, 0, sizeof(*s)); grpc_chttp2_incoming_metadata_buffer_init(&s->parsing.incoming_metadata); grpc_chttp2_incoming_metadata_buffer_init(&s->global.incoming_metadata); grpc_sopb_init(&s->writing.sopb); grpc_sopb_init(&s->global.incoming_sopb); grpc_chttp2_data_parser_init(&s->parsing.data_parser); REF_TRANSPORT(t, "stream"); lock(t); grpc_chttp2_register_stream(t, s); if (server_data) { GPR_ASSERT(t->parsing_active); s->global.id = (gpr_uint32)(gpr_uintptr)server_data; s->global.outgoing_window = t->global.settings[GRPC_PEER_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; s->global.max_recv_bytes = s->parsing.incoming_window = s->global.incoming_window = t->global.settings[GRPC_SENT_SETTINGS] [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; *t->accepting_stream = s; grpc_chttp2_stream_map_add(&t->parsing_stream_map, s->global.id, s); s->global.in_stream_map = 1; } if (initial_op) perform_stream_op_locked(&t->global, &s->global, initial_op); unlock(t); return 0; }
static void unlock(grpc_chttp2_transport *t) { grpc_iomgr_closure *run_closures; unlock_check_read_write_state(t); if (!t->writing_active && !t->closed && grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { t->writing_active = 1; REF_TRANSPORT(t, "writing"); grpc_chttp2_schedule_closure(&t->global, &t->writing_action, 1); } run_closures = t->global.pending_closures_head; t->global.pending_closures_head = NULL; t->global.pending_closures_tail = NULL; gpr_mu_unlock(&t->mu); while (run_closures) { grpc_iomgr_closure *next = run_closures->next; run_closures->cb(run_closures->cb_arg, run_closures->success); run_closures = next; } }
static void unlock(grpc_chttp2_transport *t) { grpc_iomgr_closure *run_closures; unlock_check_read_write_state(t); if (!t->writing_active && t->global.error_state == GRPC_CHTTP2_ERROR_STATE_NONE && grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) { t->writing_active = 1; REF_TRANSPORT(t, "writing"); grpc_chttp2_schedule_closure(&t->global, &t->writing_action, 1); } /* unlock_check_parser(t); */ unlock_check_channel_callbacks(t); run_closures = t->global.pending_closures; t->global.pending_closures = NULL; gpr_mu_unlock(&t->mu); while (run_closures) { grpc_iomgr_closure *next = run_closures->next; run_closures->cb(run_closures->cb_arg, run_closures->success); run_closures = next; } }
static void init_transport(grpc_chttp2_transport *t, grpc_transport_setup_callback setup, void *arg, const grpc_channel_args *channel_args, grpc_endpoint *ep, gpr_slice *slices, size_t nslices, grpc_mdctx *mdctx, int is_client) { size_t i; int j; grpc_transport_setup_result sr; GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); memset(t, 0, sizeof(*t)); t->base.vtable = &vtable; t->ep = ep; /* one ref is for destroy, the other for when ep becomes NULL */ gpr_ref_init(&t->refs, 2); gpr_mu_init(&t->mu); grpc_mdctx_ref(mdctx); t->metadata_context = mdctx; t->endpoint_reading = 1; t->global.error_state = GRPC_CHTTP2_ERROR_STATE_NONE; t->global.next_stream_id = is_client ? 1 : 2; t->global.is_client = is_client; t->global.outgoing_window = DEFAULT_WINDOW; t->global.incoming_window = DEFAULT_WINDOW; t->global.connection_window_target = DEFAULT_CONNECTION_WINDOW_TARGET; t->global.ping_counter = 1; t->global.pings.next = t->global.pings.prev = &t->global.pings; t->parsing.is_client = is_client; t->parsing.str_grpc_timeout = grpc_mdstr_from_string(t->metadata_context, "grpc-timeout"); t->parsing.deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; t->writing.is_client = is_client; gpr_slice_buffer_init(&t->global.qbuf); gpr_slice_buffer_init(&t->writing.outbuf); grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx); grpc_iomgr_closure_init(&t->writing_action, writing_action, t); grpc_iomgr_closure_init(&t->reading_action, reading_action, t); gpr_slice_buffer_init(&t->parsing.qbuf); grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser); grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context); grpc_iomgr_closure_init(&t->channel_callback.notify_closed, notify_closed, t); if (is_client) { gpr_slice_buffer_add( &t->global.qbuf, gpr_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING)); } /* 8 is a random stab in the dark as to a good initial size: it's small enough that it shouldn't waste memory for infrequently used connections, yet large enough that the exponential growth should happen nicely when it's needed. TODO(ctiller): tune this */ grpc_chttp2_stream_map_init(&t->parsing_stream_map, 8); grpc_chttp2_stream_map_init(&t->new_stream_map, 8); /* copy in initial settings to all setting sets */ for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) { t->parsing.settings[i] = grpc_chttp2_settings_parameters[i].default_value; for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) { t->global.settings[j][i] = grpc_chttp2_settings_parameters[i].default_value; } } t->global.dirtied_local_settings = 1; /* Hack: it's common for implementations to assume 65536 bytes initial send window -- this should by rights be 0 */ t->global.force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; t->global.sent_local_settings = 0; /* configure http2 the way we like it */ if (is_client) { push_setting(t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); } push_setting(t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, DEFAULT_WINDOW); if (channel_args) { for (i = 0; i < channel_args->num_args; i++) { if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_MAX_CONCURRENT_STREAMS)) { if (is_client) { gpr_log(GPR_ERROR, "%s: is ignored on the client", GRPC_ARG_MAX_CONCURRENT_STREAMS); } else if (channel_args->args[i].type != GRPC_ARG_INTEGER) { gpr_log(GPR_ERROR, "%s: must be an integer", GRPC_ARG_MAX_CONCURRENT_STREAMS); } else { push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, channel_args->args[i].value.integer); } } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { if (channel_args->args[i].type != GRPC_ARG_INTEGER) { gpr_log(GPR_ERROR, "%s: must be an integer", GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER); } else if ((t->global.next_stream_id & 1) != (channel_args->args[i].value.integer & 1)) { gpr_log(GPR_ERROR, "%s: low bit must be %d on %s", GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, t->global.next_stream_id & 1, is_client ? "client" : "server"); } else { t->global.next_stream_id = channel_args->args[i].value.integer; } } } } gpr_mu_lock(&t->mu); t->channel_callback.executing = 1; REF_TRANSPORT(t, "init"); /* matches unref at end of this function */ gpr_mu_unlock(&t->mu); sr = setup(arg, &t->base, t->metadata_context); lock(t); t->channel_callback.cb = sr.callbacks; t->channel_callback.cb_user_data = sr.user_data; t->channel_callback.executing = 0; unlock(t); REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); UNREF_TRANSPORT(t, "init"); }
void grpc_chttp2_transport_start_reading(grpc_transport *transport, gpr_slice *slices, size_t nslices) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; REF_TRANSPORT(t, "recv_data"); /* matches unref inside recv_data */ recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK); }
/* tcp read callback */ static int recv_data_loop(grpc_chttp2_transport *t, int *success) { size_t i; int keep_reading = 0; lock(t); i = 0; GPR_ASSERT(!t->parsing_active); if (!t->closed) { t->parsing_active = 1; /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); grpc_chttp2_prepare_to_read(&t->global, &t->parsing); gpr_mu_unlock(&t->mu); for (; i < t->read_buffer.count && grpc_chttp2_perform_read(&t->parsing, t->read_buffer.slices[i]); i++) ; gpr_mu_lock(&t->mu); if (i != t->read_buffer.count) { drop_connection(t); } /* merge stream lists */ grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); t->global.concurrent_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map); if (t->parsing.initial_window_update != 0) { grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, update_global_window, t); t->parsing.initial_window_update = 0; } /* handle higher level things */ grpc_chttp2_publish_reads(&t->global, &t->parsing); t->parsing_active = 0; } if (!*success || i != t->read_buffer.count) { drop_connection(t); read_error_locked(t); } else if (!t->closed) { keep_reading = 1; REF_TRANSPORT(t, "keep_reading"); prevent_endpoint_shutdown(t); } gpr_slice_buffer_reset_and_unref(&t->read_buffer); unlock(t); if (keep_reading) { int ret = -1; switch (grpc_endpoint_read(t->ep, &t->read_buffer, &t->recv_data)) { case GRPC_ENDPOINT_DONE: *success = 1; ret = 1; break; case GRPC_ENDPOINT_ERROR: *success = 0; ret = 1; break; case GRPC_ENDPOINT_PENDING: ret = 0; break; } allow_endpoint_shutdown_unlocked(t); UNREF_TRANSPORT(t, "keep_reading"); return ret; } else { UNREF_TRANSPORT(t, "recv_data"); return 0; } gpr_log(GPR_ERROR, "should never reach here"); abort(); }