|
|
|
@ -48,6 +48,7 @@ |
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/status_conversion.h" |
|
|
|
|
#include "src/core/ext/transport/chttp2/transport/timeout_encoding.h" |
|
|
|
|
#include "src/core/lib/http/parser.h" |
|
|
|
|
#include "src/core/lib/iomgr/workqueue.h" |
|
|
|
|
#include "src/core/lib/profiling/timers.h" |
|
|
|
|
#include "src/core/lib/support/string.h" |
|
|
|
|
#include "src/core/lib/transport/static_metadata.h" |
|
|
|
@ -60,9 +61,9 @@ |
|
|
|
|
#define DEFAULT_MAX_HEADER_LIST_SIZE (16 * 1024) |
|
|
|
|
|
|
|
|
|
#define MAX_CLIENT_STREAM_ID 0x7fffffffu |
|
|
|
|
|
|
|
|
|
int grpc_http_trace = 0; |
|
|
|
|
int grpc_flowctl_trace = 0; |
|
|
|
|
int grpc_http_write_state_trace = 0; |
|
|
|
|
|
|
|
|
|
#define TRANSPORT_FROM_WRITING(tw) \ |
|
|
|
|
((grpc_chttp2_transport *)((char *)(tw)-offsetof(grpc_chttp2_transport, \
|
|
|
|
@ -88,10 +89,16 @@ static const grpc_transport_vtable vtable; |
|
|
|
|
static void writing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error); |
|
|
|
|
static void reading_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error); |
|
|
|
|
static void parsing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error); |
|
|
|
|
static void initiate_writing(grpc_exec_ctx *exec_ctx, void *t, |
|
|
|
|
grpc_error *error); |
|
|
|
|
|
|
|
|
|
static void start_writing(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t); |
|
|
|
|
static void end_waiting_for_write(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, grpc_error *error); |
|
|
|
|
|
|
|
|
|
/** Set a transport level setting, and push it to our peer */ |
|
|
|
|
static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, |
|
|
|
|
uint32_t value); |
|
|
|
|
static void push_setting(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_setting_id id, uint32_t value); |
|
|
|
|
|
|
|
|
|
/** Start disconnection chain */ |
|
|
|
|
static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
@ -137,7 +144,7 @@ static void check_read_ops(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport_global *transport_global); |
|
|
|
|
|
|
|
|
|
static void incoming_byte_stream_update_flow_control( |
|
|
|
|
grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_chttp2_stream_global *stream_global, size_t max_size_hint, |
|
|
|
|
size_t have_already); |
|
|
|
|
static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
@ -201,6 +208,7 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx, |
|
|
|
|
gpr_free(t); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*#define REFCOUNTING_DEBUG 1*/ |
|
|
|
|
#ifdef REFCOUNTING_DEBUG |
|
|
|
|
#define REF_TRANSPORT(t, r) ref_transport(t, r, __FILE__, __LINE__) |
|
|
|
|
#define UNREF_TRANSPORT(cl, t, r) unref_transport(cl, t, r, __FILE__, __LINE__) |
|
|
|
@ -231,7 +239,7 @@ static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } |
|
|
|
|
|
|
|
|
|
static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
const grpc_channel_args *channel_args, |
|
|
|
|
grpc_endpoint *ep, uint8_t is_client) { |
|
|
|
|
grpc_endpoint *ep, bool is_client) { |
|
|
|
|
size_t i; |
|
|
|
|
int j; |
|
|
|
|
|
|
|
|
@ -273,6 +281,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_closure_init(&t->writing_action, writing_action, t); |
|
|
|
|
grpc_closure_init(&t->reading_action, reading_action, t); |
|
|
|
|
grpc_closure_init(&t->parsing_action, parsing_action, t); |
|
|
|
|
grpc_closure_init(&t->initiate_writing, initiate_writing, t); |
|
|
|
|
|
|
|
|
|
gpr_slice_buffer_init(&t->parsing.qbuf); |
|
|
|
|
grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser); |
|
|
|
@ -286,6 +295,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
gpr_slice_buffer_add( |
|
|
|
|
&t->global.qbuf, |
|
|
|
|
gpr_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING)); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "initial_write"); |
|
|
|
|
} |
|
|
|
|
/* 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 |
|
|
|
@ -311,11 +321,12 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
|
|
|
|
|
/* 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(exec_ctx, t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); |
|
|
|
|
} |
|
|
|
|
push_setting(t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, DEFAULT_WINDOW); |
|
|
|
|
push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, |
|
|
|
|
DEFAULT_WINDOW); |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, |
|
|
|
|
DEFAULT_MAX_HEADER_LIST_SIZE); |
|
|
|
|
|
|
|
|
|
if (channel_args) { |
|
|
|
@ -329,7 +340,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
gpr_log(GPR_ERROR, "%s: must be an integer", |
|
|
|
|
GRPC_ARG_MAX_CONCURRENT_STREAMS); |
|
|
|
|
} else { |
|
|
|
|
push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, |
|
|
|
|
(uint32_t)channel_args->args[i].value.integer); |
|
|
|
|
} |
|
|
|
|
} else if (0 == strcmp(channel_args->args[i].key, |
|
|
|
@ -368,7 +379,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
gpr_log(GPR_ERROR, "%s: must be non-negative", |
|
|
|
|
GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER); |
|
|
|
|
} else { |
|
|
|
|
push_setting(t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, |
|
|
|
|
(uint32_t)channel_args->args[i].value.integer); |
|
|
|
|
} |
|
|
|
|
} else if (0 == strcmp(channel_args->args[i].key, |
|
|
|
@ -393,7 +404,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
gpr_log(GPR_ERROR, "%s: must be non-negative", |
|
|
|
|
GRPC_ARG_MAX_METADATA_SIZE); |
|
|
|
|
} else { |
|
|
|
|
push_setting(t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, |
|
|
|
|
push_setting(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, |
|
|
|
|
(uint32_t)channel_args->args[i].value.integer); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -444,6 +455,9 @@ static void close_transport_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, |
|
|
|
|
grpc_error *error) { |
|
|
|
|
if (!t->closed) { |
|
|
|
|
if (grpc_http_write_state_trace) { |
|
|
|
|
gpr_log(GPR_DEBUG, "W:%p close transport", t); |
|
|
|
|
} |
|
|
|
|
t->closed = 1; |
|
|
|
|
connectivity_state_set(exec_ctx, &t->global, GRPC_CHANNEL_SHUTDOWN, |
|
|
|
|
GRPC_ERROR_REF(error), "close_transport"); |
|
|
|
@ -589,7 +603,8 @@ static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_incoming_metadata_buffer_destroy( |
|
|
|
|
&s->global.received_trailing_metadata); |
|
|
|
|
gpr_slice_buffer_destroy(&s->writing.flow_controlled_buffer); |
|
|
|
|
GRPC_ERROR_UNREF(s->global.removal_error); |
|
|
|
|
GRPC_ERROR_UNREF(s->global.read_closed_error); |
|
|
|
|
GRPC_ERROR_UNREF(s->global.write_closed_error); |
|
|
|
|
|
|
|
|
|
UNREF_TRANSPORT(exec_ctx, t, "stream"); |
|
|
|
|
|
|
|
|
@ -633,6 +648,36 @@ grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( |
|
|
|
|
* LOCK MANAGEMENT |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static const char *write_state_name(grpc_chttp2_write_state state) { |
|
|
|
|
switch (state) { |
|
|
|
|
case GRPC_CHTTP2_WRITING_INACTIVE: |
|
|
|
|
return "INACTIVE"; |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER: |
|
|
|
|
return "REQUESTED[p=0]"; |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER: |
|
|
|
|
return "REQUESTED[p=1]"; |
|
|
|
|
case GRPC_CHTTP2_WRITE_SCHEDULED: |
|
|
|
|
return "SCHEDULED"; |
|
|
|
|
case GRPC_CHTTP2_WRITING: |
|
|
|
|
return "WRITING"; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER: |
|
|
|
|
return "WRITING[p=1]"; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_NO_POLLER: |
|
|
|
|
return "WRITING[p=0]"; |
|
|
|
|
} |
|
|
|
|
GPR_UNREACHABLE_CODE(return "UNKNOWN"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void set_write_state(grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_write_state state, const char *reason) { |
|
|
|
|
if (grpc_http_write_state_trace) { |
|
|
|
|
gpr_log(GPR_DEBUG, "W:%p %s -> %s because %s", t, |
|
|
|
|
write_state_name(t->executor.write_state), write_state_name(state), |
|
|
|
|
reason); |
|
|
|
|
} |
|
|
|
|
t->executor.write_state = state; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void finish_global_actions(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t) { |
|
|
|
|
grpc_chttp2_executor_action_header *hdr; |
|
|
|
@ -641,13 +686,6 @@ static void finish_global_actions(grpc_exec_ctx *exec_ctx, |
|
|
|
|
GPR_TIMER_BEGIN("finish_global_actions", 0); |
|
|
|
|
|
|
|
|
|
for (;;) { |
|
|
|
|
if (!t->executor.writing_active && !t->closed && |
|
|
|
|
grpc_chttp2_unlocking_check_writes(exec_ctx, &t->global, &t->writing)) { |
|
|
|
|
t->executor.writing_active = 1; |
|
|
|
|
REF_TRANSPORT(t, "writing"); |
|
|
|
|
prevent_endpoint_shutdown(t); |
|
|
|
|
grpc_exec_ctx_sched(exec_ctx, &t->writing_action, GRPC_ERROR_NONE, NULL); |
|
|
|
|
} |
|
|
|
|
check_read_ops(exec_ctx, &t->global); |
|
|
|
|
|
|
|
|
|
gpr_mu_lock(&t->executor.mu); |
|
|
|
@ -668,8 +706,27 @@ static void finish_global_actions(grpc_exec_ctx *exec_ctx, |
|
|
|
|
continue; |
|
|
|
|
} else { |
|
|
|
|
t->executor.global_active = false; |
|
|
|
|
switch (t->executor.write_state) { |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER: |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITE_SCHEDULED, "unlocking"); |
|
|
|
|
REF_TRANSPORT(t, "initiate_writing"); |
|
|
|
|
gpr_mu_unlock(&t->executor.mu); |
|
|
|
|
grpc_exec_ctx_sched(exec_ctx, &t->initiate_writing, GRPC_ERROR_NONE, |
|
|
|
|
grpc_endpoint_get_workqueue(t->ep)); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER: |
|
|
|
|
start_writing(exec_ctx, t); |
|
|
|
|
gpr_mu_unlock(&t->executor.mu); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING_INACTIVE: |
|
|
|
|
case GRPC_CHTTP2_WRITING: |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER: |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_NO_POLLER: |
|
|
|
|
case GRPC_CHTTP2_WRITE_SCHEDULED: |
|
|
|
|
gpr_mu_unlock(&t->executor.mu); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
gpr_mu_unlock(&t->executor.mu); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -740,16 +797,99 @@ void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
* OUTPUT PROCESSING |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
void grpc_chttp2_become_writable(grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_chttp2_stream_global *stream_global) { |
|
|
|
|
void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport_global *transport_global, |
|
|
|
|
bool covered_by_poller, const char *reason) { |
|
|
|
|
grpc_chttp2_transport *t = TRANSPORT_FROM_GLOBAL(transport_global); |
|
|
|
|
switch (t->executor.write_state) { |
|
|
|
|
case GRPC_CHTTP2_WRITING_INACTIVE: |
|
|
|
|
set_write_state(t, covered_by_poller |
|
|
|
|
? GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER |
|
|
|
|
: GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER, |
|
|
|
|
reason); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER: |
|
|
|
|
/* nothing to do: write already requested */ |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER: |
|
|
|
|
if (covered_by_poller) { |
|
|
|
|
/* upgrade to note poller is available to cover the write */ |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER, reason); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITE_SCHEDULED: |
|
|
|
|
/* nothing to do: write already scheduled */ |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING: |
|
|
|
|
set_write_state(t, |
|
|
|
|
covered_by_poller ? GRPC_CHTTP2_WRITING_STALE_WITH_POLLER |
|
|
|
|
: GRPC_CHTTP2_WRITING_STALE_NO_POLLER, |
|
|
|
|
reason); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER: |
|
|
|
|
/* nothing to do: write already requested */ |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_NO_POLLER: |
|
|
|
|
if (covered_by_poller) { |
|
|
|
|
/* upgrade to note poller is available to cover the write */ |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITING_STALE_WITH_POLLER, reason); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void start_writing(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { |
|
|
|
|
GPR_ASSERT(t->executor.write_state == GRPC_CHTTP2_WRITE_SCHEDULED || |
|
|
|
|
t->executor.write_state == GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER); |
|
|
|
|
if (!t->closed && |
|
|
|
|
grpc_chttp2_unlocking_check_writes(exec_ctx, &t->global, &t->writing)) { |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITING, "start_writing"); |
|
|
|
|
REF_TRANSPORT(t, "writing"); |
|
|
|
|
prevent_endpoint_shutdown(t); |
|
|
|
|
grpc_exec_ctx_sched(exec_ctx, &t->writing_action, GRPC_ERROR_NONE, NULL); |
|
|
|
|
} else { |
|
|
|
|
if (t->closed) { |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE, |
|
|
|
|
"start_writing:transport_closed"); |
|
|
|
|
} else { |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE, |
|
|
|
|
"start_writing:nothing_to_write"); |
|
|
|
|
} |
|
|
|
|
end_waiting_for_write(exec_ctx, t, GRPC_ERROR_CREATE("Nothing to write")); |
|
|
|
|
if (t->ep && !t->endpoint_reading) { |
|
|
|
|
destroy_endpoint(exec_ctx, t); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void initiate_writing_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_stream *s_unused, |
|
|
|
|
void *arg_ignored) { |
|
|
|
|
start_writing(exec_ctx, t); |
|
|
|
|
UNREF_TRANSPORT(exec_ctx, t, "initiate_writing"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void initiate_writing(grpc_exec_ctx *exec_ctx, void *arg, |
|
|
|
|
grpc_error *error) { |
|
|
|
|
grpc_chttp2_run_with_global_lock(exec_ctx, arg, NULL, initiate_writing_locked, |
|
|
|
|
NULL, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_chttp2_become_writable(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_chttp2_stream_global *stream_global, |
|
|
|
|
bool covered_by_poller, const char *reason) { |
|
|
|
|
if (!TRANSPORT_FROM_GLOBAL(transport_global)->closed && |
|
|
|
|
grpc_chttp2_list_add_writable_stream(transport_global, stream_global)) { |
|
|
|
|
GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_writing"); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, transport_global, covered_by_poller, |
|
|
|
|
reason); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, |
|
|
|
|
uint32_t value) { |
|
|
|
|
static void push_setting(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_setting_id id, uint32_t value) { |
|
|
|
|
const grpc_chttp2_setting_parameters *sp = |
|
|
|
|
&grpc_chttp2_settings_parameters[id]; |
|
|
|
|
uint32_t use_value = GPR_CLAMP(value, sp->min_value, sp->max_value); |
|
|
|
@ -760,9 +900,22 @@ static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, |
|
|
|
|
if (use_value != t->global.settings[GRPC_LOCAL_SETTINGS][id]) { |
|
|
|
|
t->global.settings[GRPC_LOCAL_SETTINGS][id] = use_value; |
|
|
|
|
t->global.dirtied_local_settings = 1; |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "push_setting"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void end_waiting_for_write(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, grpc_error *error) { |
|
|
|
|
grpc_chttp2_stream_global *stream_global; |
|
|
|
|
while (grpc_chttp2_list_pop_closed_waiting_for_writing(&t->global, |
|
|
|
|
&stream_global)) { |
|
|
|
|
fail_pending_writes(exec_ctx, &t->global, stream_global, |
|
|
|
|
GRPC_ERROR_REF(error)); |
|
|
|
|
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "finish_writes"); |
|
|
|
|
} |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void terminate_writing_with_lock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_stream *s_ignored, |
|
|
|
@ -777,24 +930,32 @@ static void terminate_writing_with_lock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
|
|
|
|
|
grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing); |
|
|
|
|
|
|
|
|
|
grpc_chttp2_stream_global *stream_global; |
|
|
|
|
while (grpc_chttp2_list_pop_closed_waiting_for_writing(&t->global, |
|
|
|
|
&stream_global)) { |
|
|
|
|
fail_pending_writes(exec_ctx, &t->global, stream_global, |
|
|
|
|
GRPC_ERROR_REF(error)); |
|
|
|
|
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "finish_writes"); |
|
|
|
|
end_waiting_for_write(exec_ctx, t, error); |
|
|
|
|
|
|
|
|
|
switch (t->executor.write_state) { |
|
|
|
|
case GRPC_CHTTP2_WRITING_INACTIVE: |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER: |
|
|
|
|
case GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER: |
|
|
|
|
case GRPC_CHTTP2_WRITE_SCHEDULED: |
|
|
|
|
GPR_UNREACHABLE_CODE(break); |
|
|
|
|
case GRPC_CHTTP2_WRITING: |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITING_INACTIVE, "terminate_writing"); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_WITH_POLLER: |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_WITH_POLLER, |
|
|
|
|
"terminate_writing"); |
|
|
|
|
break; |
|
|
|
|
case GRPC_CHTTP2_WRITING_STALE_NO_POLLER: |
|
|
|
|
set_write_state(t, GRPC_CHTTP2_WRITE_REQUESTED_NO_POLLER, |
|
|
|
|
"terminate_writing"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* leave the writing flag up on shutdown to prevent further writes in
|
|
|
|
|
unlock() |
|
|
|
|
from starting */ |
|
|
|
|
t->executor.writing_active = 0; |
|
|
|
|
if (t->ep && !t->endpoint_reading) { |
|
|
|
|
destroy_endpoint(exec_ctx, t); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
UNREF_TRANSPORT(exec_ctx, t, "writing"); |
|
|
|
|
GRPC_ERROR_UNREF(error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, |
|
|
|
@ -877,7 +1038,8 @@ static void maybe_start_some_streams( |
|
|
|
|
stream_global->id, STREAM_FROM_GLOBAL(stream_global)); |
|
|
|
|
stream_global->in_stream_map = true; |
|
|
|
|
transport_global->concurrent_stream_count++; |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global, true, |
|
|
|
|
"new_stream"); |
|
|
|
|
} |
|
|
|
|
/* cancel out streams that will never be started */ |
|
|
|
|
while (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID && |
|
|
|
@ -1012,9 +1174,11 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
maybe_start_some_streams(exec_ctx, transport_global); |
|
|
|
|
} else { |
|
|
|
|
GPR_ASSERT(stream_global->id != 0); |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global, |
|
|
|
|
true, "op.send_initial_metadata"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
stream_global->send_trailing_metadata = NULL; |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, transport_global, stream_global, |
|
|
|
|
&stream_global->send_initial_metadata_finished, |
|
|
|
@ -1036,7 +1200,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} else { |
|
|
|
|
stream_global->send_message = op->send_message; |
|
|
|
|
if (stream_global->id != 0) { |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global, |
|
|
|
|
true, "op.send_message"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1069,6 +1234,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); |
|
|
|
|
} |
|
|
|
|
if (stream_global->write_closed) { |
|
|
|
|
stream_global->send_trailing_metadata = NULL; |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, transport_global, stream_global, |
|
|
|
|
&stream_global->send_trailing_metadata_finished, |
|
|
|
@ -1079,7 +1245,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} else if (stream_global->id != 0) { |
|
|
|
|
/* TODO(ctiller): check if there's flow control for any outstanding
|
|
|
|
|
bytes before going writable */ |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global, |
|
|
|
|
true, "op.send_trailing_metadata"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1100,8 +1267,8 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
(stream_global->incoming_frames.head == NULL || |
|
|
|
|
stream_global->incoming_frames.head->is_tail)) { |
|
|
|
|
incoming_byte_stream_update_flow_control( |
|
|
|
|
transport_global, stream_global, transport_global->stream_lookahead, |
|
|
|
|
0); |
|
|
|
|
exec_ctx, transport_global, stream_global, |
|
|
|
|
transport_global->stream_lookahead, 0); |
|
|
|
|
} |
|
|
|
|
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); |
|
|
|
|
} |
|
|
|
@ -1129,7 +1296,8 @@ static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, |
|
|
|
|
sizeof(*op)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void send_ping_locked(grpc_chttp2_transport *t, grpc_closure *on_recv) { |
|
|
|
|
static void send_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_closure *on_recv) { |
|
|
|
|
grpc_chttp2_outstanding_ping *p = gpr_malloc(sizeof(*p)); |
|
|
|
|
p->next = &t->global.pings; |
|
|
|
|
p->prev = p->next->prev; |
|
|
|
@ -1144,6 +1312,7 @@ static void send_ping_locked(grpc_chttp2_transport *t, grpc_closure *on_recv) { |
|
|
|
|
p->id[7] = (uint8_t)(t->global.ping_counter & 0xff); |
|
|
|
|
p->on_recv = on_recv; |
|
|
|
|
gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, &t->global, true, "send_ping"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void ack_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
@ -1203,6 +1372,7 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
close_transport = grpc_chttp2_has_streams(t) |
|
|
|
|
? GRPC_ERROR_NONE |
|
|
|
|
: GRPC_ERROR_CREATE("GOAWAY sent"); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, &t->global, false, "goaway_sent"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->set_accept_stream) { |
|
|
|
@ -1220,7 +1390,7 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (op->send_ping) { |
|
|
|
|
send_ping_locked(t, op->send_ping); |
|
|
|
|
send_ping_locked(exec_ctx, t, op->send_ping); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (close_transport != GRPC_ERROR_NONE) { |
|
|
|
@ -1407,6 +1577,8 @@ static void cancel_from_api(grpc_exec_ctx *exec_ctx, |
|
|
|
|
&transport_global->qbuf, |
|
|
|
|
grpc_chttp2_rst_stream_create(stream_global->id, (uint32_t)http_error, |
|
|
|
|
&stream_global->stats.outgoing)); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, transport_global, false, |
|
|
|
|
"rst_stream"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *msg = |
|
|
|
@ -1466,10 +1638,38 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) { |
|
|
|
|
if (error == GRPC_ERROR_NONE) return; |
|
|
|
|
for (size_t i = 0; i < *nrefs; i++) { |
|
|
|
|
if (error == refs[i]) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
refs[*nrefs] = error; |
|
|
|
|
++*nrefs; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_error *removal_error(grpc_error *extra_error, |
|
|
|
|
grpc_chttp2_stream_global *stream_global) { |
|
|
|
|
grpc_error *refs[3]; |
|
|
|
|
size_t nrefs = 0; |
|
|
|
|
add_error(stream_global->read_closed_error, refs, &nrefs); |
|
|
|
|
add_error(stream_global->write_closed_error, refs, &nrefs); |
|
|
|
|
add_error(extra_error, refs, &nrefs); |
|
|
|
|
grpc_error *error = GRPC_ERROR_NONE; |
|
|
|
|
if (nrefs > 0) { |
|
|
|
|
error = GRPC_ERROR_CREATE_REFERENCING("Failed due to stream removal", refs, |
|
|
|
|
nrefs); |
|
|
|
|
} |
|
|
|
|
GRPC_ERROR_UNREF(extra_error); |
|
|
|
|
return error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void fail_pending_writes(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_chttp2_stream_global *stream_global, |
|
|
|
|
grpc_error *error) { |
|
|
|
|
error = removal_error(error, stream_global); |
|
|
|
|
grpc_chttp2_complete_closure_step( |
|
|
|
|
exec_ctx, transport_global, stream_global, |
|
|
|
|
&stream_global->send_initial_metadata_finished, GRPC_ERROR_REF(error)); |
|
|
|
@ -1492,14 +1692,17 @@ void grpc_chttp2_mark_stream_closed( |
|
|
|
|
} |
|
|
|
|
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); |
|
|
|
|
if (close_reads && !stream_global->read_closed) { |
|
|
|
|
stream_global->read_closed_error = GRPC_ERROR_REF(error); |
|
|
|
|
stream_global->read_closed = true; |
|
|
|
|
stream_global->published_initial_metadata = true; |
|
|
|
|
stream_global->published_trailing_metadata = true; |
|
|
|
|
decrement_active_streams_locked(exec_ctx, transport_global, stream_global); |
|
|
|
|
} |
|
|
|
|
if (close_writes && !stream_global->write_closed) { |
|
|
|
|
stream_global->write_closed_error = GRPC_ERROR_REF(error); |
|
|
|
|
stream_global->write_closed = true; |
|
|
|
|
if (TRANSPORT_FROM_GLOBAL(transport_global)->executor.writing_active) { |
|
|
|
|
if (TRANSPORT_FROM_GLOBAL(transport_global)->executor.write_state != |
|
|
|
|
GRPC_CHTTP2_WRITING_INACTIVE) { |
|
|
|
|
GRPC_CHTTP2_STREAM_REF(stream_global, "finish_writes"); |
|
|
|
|
grpc_chttp2_list_add_closed_waiting_for_writing(transport_global, |
|
|
|
|
stream_global); |
|
|
|
@ -1509,7 +1712,6 @@ void grpc_chttp2_mark_stream_closed( |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (stream_global->read_closed && stream_global->write_closed) { |
|
|
|
|
stream_global->removal_error = GRPC_ERROR_REF(error); |
|
|
|
|
if (stream_global->id != 0 && |
|
|
|
|
TRANSPORT_FROM_GLOBAL(transport_global)->executor.parsing_active) { |
|
|
|
|
grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global, |
|
|
|
@ -1517,7 +1719,8 @@ void grpc_chttp2_mark_stream_closed( |
|
|
|
|
} else { |
|
|
|
|
if (stream_global->id != 0) { |
|
|
|
|
remove_stream(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global), |
|
|
|
|
stream_global->id, GRPC_ERROR_REF(error)); |
|
|
|
|
stream_global->id, |
|
|
|
|
removal_error(GRPC_ERROR_REF(error), stream_global)); |
|
|
|
|
} |
|
|
|
|
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); |
|
|
|
|
} |
|
|
|
@ -1641,6 +1844,8 @@ static void close_from_api(grpc_exec_ctx *exec_ctx, |
|
|
|
|
|
|
|
|
|
grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1, |
|
|
|
|
1, error); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, transport_global, false, |
|
|
|
|
"close_from_api"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
@ -1670,8 +1875,14 @@ static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** update window from a settings change */ |
|
|
|
|
typedef struct { |
|
|
|
|
grpc_chttp2_transport *t; |
|
|
|
|
grpc_exec_ctx *exec_ctx; |
|
|
|
|
} update_global_window_args; |
|
|
|
|
|
|
|
|
|
static void update_global_window(void *args, uint32_t id, void *stream) { |
|
|
|
|
grpc_chttp2_transport *t = args; |
|
|
|
|
update_global_window_args *a = args; |
|
|
|
|
grpc_chttp2_transport *t = a->t; |
|
|
|
|
grpc_chttp2_stream *s = stream; |
|
|
|
|
grpc_chttp2_transport_global *transport_global = &t->global; |
|
|
|
|
grpc_chttp2_stream_global *stream_global = &s->global; |
|
|
|
@ -1685,7 +1896,8 @@ static void update_global_window(void *args, uint32_t id, void *stream) { |
|
|
|
|
is_zero = stream_global->outgoing_window <= 0; |
|
|
|
|
|
|
|
|
|
if (was_zero && !is_zero) { |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(a->exec_ctx, transport_global, stream_global, |
|
|
|
|
true, "update_global_window"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1794,14 +2006,19 @@ static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
grpc_chttp2_transport_global *transport_global = &t->global; |
|
|
|
|
grpc_chttp2_transport_parsing *transport_parsing = &t->parsing; |
|
|
|
|
/* copy parsing qbuf to global qbuf */ |
|
|
|
|
gpr_slice_buffer_move_into(&t->parsing.qbuf, &t->global.qbuf); |
|
|
|
|
if (t->parsing.qbuf.count > 0) { |
|
|
|
|
gpr_slice_buffer_move_into(&t->parsing.qbuf, &t->global.qbuf); |
|
|
|
|
grpc_chttp2_initiate_write(exec_ctx, transport_global, false, |
|
|
|
|
"parsing_qbuf"); |
|
|
|
|
} |
|
|
|
|
/* merge stream lists */ |
|
|
|
|
grpc_chttp2_stream_map_move_into(&t->new_stream_map, &t->parsing_stream_map); |
|
|
|
|
transport_global->concurrent_stream_count = |
|
|
|
|
(uint32_t)grpc_chttp2_stream_map_size(&t->parsing_stream_map); |
|
|
|
|
if (transport_parsing->initial_window_update != 0) { |
|
|
|
|
update_global_window_args args = {t, exec_ctx}; |
|
|
|
|
grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, |
|
|
|
|
update_global_window, t); |
|
|
|
|
update_global_window, &args); |
|
|
|
|
transport_parsing->initial_window_update = 0; |
|
|
|
|
} |
|
|
|
|
/* handle higher level things */ |
|
|
|
@ -1824,7 +2041,7 @@ static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, |
|
|
|
|
GPR_ASSERT(stream_global->write_closed); |
|
|
|
|
GPR_ASSERT(stream_global->read_closed); |
|
|
|
|
remove_stream(exec_ctx, t, stream_global->id, |
|
|
|
|
GRPC_ERROR_REF(stream_global->removal_error)); |
|
|
|
|
removal_error(GRPC_ERROR_NONE, stream_global)); |
|
|
|
|
GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1847,11 +2064,12 @@ static void post_reading_action_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
drop_connection(exec_ctx, t, GRPC_ERROR_REF(error)); |
|
|
|
|
t->endpoint_reading = 0; |
|
|
|
|
if (!t->executor.writing_active && t->ep) { |
|
|
|
|
grpc_endpoint_destroy(exec_ctx, t->ep); |
|
|
|
|
t->ep = NULL; |
|
|
|
|
/* safe as we still have a ref for read */ |
|
|
|
|
UNREF_TRANSPORT(exec_ctx, t, "disconnect"); |
|
|
|
|
if (grpc_http_write_state_trace) { |
|
|
|
|
gpr_log(GPR_DEBUG, "R:%p -> 0 ws=%s", t, |
|
|
|
|
write_state_name(t->executor.write_state)); |
|
|
|
|
} |
|
|
|
|
if (t->executor.write_state == GRPC_CHTTP2_WRITING_INACTIVE && t->ep) { |
|
|
|
|
destroy_endpoint(exec_ctx, t); |
|
|
|
|
} |
|
|
|
|
} else if (!t->closed) { |
|
|
|
|
keep_reading = true; |
|
|
|
@ -1935,7 +2153,7 @@ static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void incoming_byte_stream_update_flow_control( |
|
|
|
|
grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, |
|
|
|
|
grpc_chttp2_stream_global *stream_global, size_t max_size_hint, |
|
|
|
|
size_t have_already) { |
|
|
|
|
uint32_t max_recv_bytes; |
|
|
|
@ -1970,7 +2188,8 @@ static void incoming_byte_stream_update_flow_control( |
|
|
|
|
add_max_recv_bytes); |
|
|
|
|
grpc_chttp2_list_add_unannounced_incoming_window_available(transport_global, |
|
|
|
|
stream_global); |
|
|
|
|
grpc_chttp2_become_writable(transport_global, stream_global); |
|
|
|
|
grpc_chttp2_become_writable(exec_ctx, transport_global, stream_global, |
|
|
|
|
false, "read_incoming_stream"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1992,8 +2211,9 @@ static void incoming_byte_stream_next_locked(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_chttp2_stream_global *stream_global = &bs->stream->global; |
|
|
|
|
|
|
|
|
|
if (bs->is_tail) { |
|
|
|
|
incoming_byte_stream_update_flow_control( |
|
|
|
|
transport_global, stream_global, arg->max_size_hint, bs->slices.length); |
|
|
|
|
incoming_byte_stream_update_flow_control(exec_ctx, transport_global, |
|
|
|
|
stream_global, arg->max_size_hint, |
|
|
|
|
bs->slices.length); |
|
|
|
|
} |
|
|
|
|
if (bs->slices.count > 0) { |
|
|
|
|
*arg->slice = gpr_slice_buffer_take_first(&bs->slices); |
|
|
|
@ -2177,7 +2397,7 @@ static char *format_flowctl_context_var(const char *context, const char *var, |
|
|
|
|
if (context == NULL) { |
|
|
|
|
*scope = NULL; |
|
|
|
|
gpr_asprintf(&buf, "%s(%" PRId64 ")", var, val); |
|
|
|
|
result = gpr_leftpad(buf, ' ', 40); |
|
|
|
|
result = gpr_leftpad(buf, ' ', 60); |
|
|
|
|
gpr_free(buf); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
@ -2190,7 +2410,7 @@ static char *format_flowctl_context_var(const char *context, const char *var, |
|
|
|
|
gpr_free(tmp); |
|
|
|
|
} |
|
|
|
|
gpr_asprintf(&buf, "%s.%s(%" PRId64 ")", underscore_pos + 1, var, val); |
|
|
|
|
result = gpr_leftpad(buf, ' ', 40); |
|
|
|
|
result = gpr_leftpad(buf, ' ', 60); |
|
|
|
|
gpr_free(buf); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
@ -2223,7 +2443,7 @@ void grpc_chttp2_flowctl_trace(const char *file, int line, const char *phase, |
|
|
|
|
|
|
|
|
|
tmp_phase = gpr_leftpad(phase, ' ', 8); |
|
|
|
|
tmp_scope1 = gpr_leftpad(scope1, ' ', 11); |
|
|
|
|
gpr_asprintf(&prefix, "FLOW %s: %s %s ", phase, clisvr, scope1); |
|
|
|
|
gpr_asprintf(&prefix, "FLOW %s: %s %s ", tmp_phase, clisvr, scope1); |
|
|
|
|
gpr_free(tmp_phase); |
|
|
|
|
gpr_free(tmp_scope1); |
|
|
|
|
|
|
|
|
|