Merge branch 'minimal' into minimal_test

reviewable/pr10449/r2
Craig Tiller 8 years ago
commit 29ebc57554
  1. 1
      doc/environment_variables.md
  2. 1
      include/grpc/impl/codegen/atm_gcc_atomic.h
  3. 1
      include/grpc/impl/codegen/atm_gcc_sync.h
  4. 1
      include/grpc/impl/codegen/atm_windows.h
  5. 2
      include/grpc/impl/codegen/port_platform.h
  6. 2
      include/grpc/support/tls.h
  7. 20
      src/core/ext/census/grpc_filter.c
  8. 110
      src/core/ext/filters/client_channel/client_channel.c
  9. 4
      src/core/ext/filters/client_channel/subchannel.c
  10. 2
      src/core/ext/filters/client_channel/subchannel.h
  11. 44
      src/core/ext/filters/deadline/deadline_filter.c
  12. 6
      src/core/ext/filters/deadline/deadline_filter.h
  13. 123
      src/core/ext/filters/http/client/http_client_filter.c
  14. 36
      src/core/ext/filters/http/compress/compress_filter.c
  15. 82
      src/core/ext/filters/http/server/http_server_filter.c
  16. 21
      src/core/ext/filters/load_reporting/load_reporting_filter.c
  17. 29
      src/core/ext/filters/message_size/message_size_filter.c
  18. 91
      src/core/ext/transport/chttp2/transport/chttp2_transport.c
  19. 99
      src/core/ext/transport/cronet/transport/cronet_transport.c
  20. 11
      src/core/lib/channel/channel_stack.c
  21. 11
      src/core/lib/channel/channel_stack.h
  22. 8
      src/core/lib/channel/connected_channel.c
  23. 5
      src/core/lib/iomgr/ev_epoll_linux.c
  24. 2
      src/core/lib/iomgr/ev_poll_posix.c
  25. 3
      src/core/lib/iomgr/timer.h
  26. 320
      src/core/lib/iomgr/timer_generic.c
  27. 2
      src/core/lib/iomgr/timer_generic.h
  28. 16
      src/core/lib/iomgr/timer_heap.c
  29. 38
      src/core/lib/security/transport/client_auth_filter.c
  30. 26
      src/core/lib/security/transport/server_auth_filter.c
  31. 167
      src/core/lib/surface/call.c
  32. 20
      src/core/lib/surface/lame_client.c
  33. 44
      src/core/lib/surface/server.c
  34. 34
      src/core/lib/transport/transport.c
  35. 121
      src/core/lib/transport/transport.h
  36. 3
      src/core/lib/transport/transport_impl.h
  37. 34
      src/core/lib/transport/transport_op_string.c
  38. 6
      src/cpp/common/channel_filter.cc
  39. 85
      src/cpp/common/channel_filter.h
  40. 3
      src/proto/grpc/testing/control.proto
  41. 2
      test/core/channel/channel_stack_test.c
  42. 15
      test/core/end2end/tests/filter_causes_close.c
  43. 2
      test/core/end2end/tests/max_connection_age.c
  44. 4
      test/core/end2end/tests/max_connection_idle.c
  45. 21
      test/core/iomgr/timer_heap_test.c
  46. 16
      test/core/iomgr/timer_list_test.c
  47. 8
      test/core/support/arena_test.c
  48. 5
      test/cpp/end2end/filter_end2end_test.cc
  49. 22
      test/cpp/microbenchmarks/bm_call_create.cc
  50. 81
      test/cpp/microbenchmarks/bm_chttp2_transport.cc
  51. 132
      test/cpp/qps/client_async.cc
  52. 18
      test/cpp/qps/client_sync.cc
  53. 982
      tools/run_tests/generated/tests.json
  54. 44
      tools/run_tests/performance/scenario_config.py

@ -55,6 +55,7 @@ some configuration as environment variables that can be set.
- queue_timeout - queue_timeout
- server_channel - lightweight trace of significant server channel events - server_channel - lightweight trace of significant server channel events
- secure_endpoint - traces bytes flowing through encrypted channels - secure_endpoint - traces bytes flowing through encrypted channels
- timer - timers (alarms) in the grpc internals
- transport_security - traces metadata about secure channel establishment - transport_security - traces metadata about secure channel establishment
- tcp - traces bytes in and out of a channel - tcp - traces bytes in and out of a channel

@ -39,6 +39,7 @@
#include <grpc/impl/codegen/port_platform.h> #include <grpc/impl/codegen/port_platform.h>
typedef intptr_t gpr_atm; typedef intptr_t gpr_atm;
#define GPR_ATM_MAX INTPTR_MAX
#ifdef GPR_LOW_LEVEL_COUNTERS #ifdef GPR_LOW_LEVEL_COUNTERS
extern gpr_atm gpr_counter_atm_cas; extern gpr_atm gpr_counter_atm_cas;

@ -39,6 +39,7 @@
#include <grpc/impl/codegen/port_platform.h> #include <grpc/impl/codegen/port_platform.h>
typedef intptr_t gpr_atm; typedef intptr_t gpr_atm;
#define GPR_ATM_MAX INTPTR_MAX
#define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory") #define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory")

@ -38,6 +38,7 @@
#include <grpc/impl/codegen/port_platform.h> #include <grpc/impl/codegen/port_platform.h>
typedef intptr_t gpr_atm; typedef intptr_t gpr_atm;
#define GPR_ATM_MAX INTPTR_MAX
#define gpr_atm_full_barrier MemoryBarrier #define gpr_atm_full_barrier MemoryBarrier

@ -375,8 +375,10 @@ typedef unsigned __int64 uint64_t;
#ifndef GRPC_MUST_USE_RESULT #ifndef GRPC_MUST_USE_RESULT
#if defined(__GNUC__) && !defined(__MINGW32__) #if defined(__GNUC__) && !defined(__MINGW32__)
#define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result)) #define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result))
#define GPR_ALIGN_STRUCT(n) __attribute__((aligned(n)))
#else #else
#define GRPC_MUST_USE_RESULT #define GRPC_MUST_USE_RESULT
#define GPR_ALIGN_STRUCT(n)
#endif #endif
#endif #endif

@ -58,7 +58,7 @@
gpr_tls_set(&foo, new_value); gpr_tls_set(&foo, new_value);
Accessing a thread local: Accessing a thread local:
current_value = gpr_tls_get(&foo, value); current_value = gpr_tls_get(&foo);
ALL functions here may be implemented as macros. */ ALL functions here may be implemented as macros. */

@ -74,17 +74,18 @@ static void extract_and_annotate_method_tag(grpc_metadata_batch *md,
} }
static void client_mutate_op(grpc_call_element *elem, static void client_mutate_op(grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
if (op->send_initial_metadata) { if (op->send_initial_metadata) {
extract_and_annotate_method_tag(op->send_initial_metadata, calld, chand); extract_and_annotate_method_tag(
op->payload->send_initial_metadata.send_initial_metadata, calld, chand);
} }
} }
static void client_start_transport_op(grpc_exec_ctx *exec_ctx, static void client_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
client_mutate_op(elem, op); client_mutate_op(elem, op);
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
} }
@ -103,19 +104,22 @@ static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr,
} }
static void server_mutate_op(grpc_call_element *elem, static void server_mutate_op(grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_initial_metadata) { if (op->recv_initial_metadata) {
/* substitute our callback for the op callback */ /* substitute our callback for the op callback */
calld->recv_initial_metadata = op->recv_initial_metadata; calld->recv_initial_metadata =
calld->on_done_recv = op->recv_initial_metadata_ready; op->payload->recv_initial_metadata.recv_initial_metadata;
op->recv_initial_metadata_ready = &calld->finish_recv; calld->on_done_recv =
op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->finish_recv;
} }
} }
static void server_start_transport_op(grpc_exec_ctx *exec_ctx, static void server_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
/* TODO(ctiller): this code fails. I don't know why. I expect it's /* TODO(ctiller): this code fails. I don't know why. I expect it's
incomplete, and someone should look at it soon. incomplete, and someone should look at it soon.

@ -534,7 +534,7 @@ static void on_resolver_result_changed_locked(grpc_exec_ctx *exec_ctx,
static void start_transport_op_locked(grpc_exec_ctx *exec_ctx, void *arg, static void start_transport_op_locked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error_ignored) { grpc_error *error_ignored) {
grpc_transport_op *op = arg; grpc_transport_op *op = arg;
grpc_channel_element *elem = op->transport_private.args[0]; grpc_channel_element *elem = op->handler_private.extra_arg;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
if (op->on_connectivity_state_change != NULL) { if (op->on_connectivity_state_change != NULL) {
@ -596,12 +596,12 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
op->bind_pollset); op->bind_pollset);
} }
op->transport_private.args[0] = elem; op->handler_private.extra_arg = elem;
GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op"); GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op");
grpc_closure_sched( grpc_closure_sched(
exec_ctx, grpc_closure_init( exec_ctx,
&op->transport_private.closure, start_transport_op_locked, grpc_closure_init(&op->handler_private.closure, start_transport_op_locked,
op, grpc_combiner_scheduler(chand->combiner, false)), op, grpc_combiner_scheduler(chand->combiner, false)),
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
} }
@ -770,7 +770,7 @@ typedef struct client_channel_call_data {
grpc_connected_subchannel *connected_subchannel; grpc_connected_subchannel *connected_subchannel;
grpc_polling_entity *pollent; grpc_polling_entity *pollent;
grpc_transport_stream_op **waiting_ops; grpc_transport_stream_op_batch **waiting_ops;
size_t waiting_ops_count; size_t waiting_ops_count;
size_t waiting_ops_capacity; size_t waiting_ops_capacity;
@ -790,7 +790,8 @@ grpc_subchannel_call *grpc_client_channel_get_subchannel_call(
return scc == CANCELLED_CALL ? NULL : scc; return scc == CANCELLED_CALL ? NULL : scc;
} }
static void add_waiting_locked(call_data *calld, grpc_transport_stream_op *op) { static void add_waiting_locked(call_data *calld,
grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("add_waiting_locked", 0); GPR_TIMER_BEGIN("add_waiting_locked", 0);
if (calld->waiting_ops_count == calld->waiting_ops_capacity) { if (calld->waiting_ops_count == calld->waiting_ops_capacity) {
calld->waiting_ops_capacity = GPR_MAX(3, 2 * calld->waiting_ops_capacity); calld->waiting_ops_capacity = GPR_MAX(3, 2 * calld->waiting_ops_capacity);
@ -806,7 +807,7 @@ static void fail_locked(grpc_exec_ctx *exec_ctx, call_data *calld,
grpc_error *error) { grpc_error *error) {
size_t i; size_t i;
for (i = 0; i < calld->waiting_ops_count; i++) { for (i = 0; i < calld->waiting_ops_count; i++) {
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, calld->waiting_ops[i], GRPC_ERROR_REF(error)); exec_ctx, calld->waiting_ops[i], GRPC_ERROR_REF(error));
} }
calld->waiting_ops_count = 0; calld->waiting_ops_count = 0;
@ -819,7 +820,7 @@ static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, call_data *calld) {
} }
grpc_subchannel_call *call = GET_CALL(calld); grpc_subchannel_call *call = GET_CALL(calld);
grpc_transport_stream_op **ops = calld->waiting_ops; grpc_transport_stream_op_batch **ops = calld->waiting_ops;
size_t nops = calld->waiting_ops_count; size_t nops = calld->waiting_ops_count;
if (call == CANCELLED_CALL) { if (call == CANCELLED_CALL) {
fail_locked(exec_ctx, calld, GRPC_ERROR_CANCELLED); fail_locked(exec_ctx, calld, GRPC_ERROR_CANCELLED);
@ -1069,9 +1070,9 @@ static bool pick_subchannel_locked(
return false; return false;
} }
static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx, static void start_transport_stream_op_batch_locked_inner(
grpc_transport_stream_op *op, grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *op,
grpc_call_element *elem) { grpc_call_element *elem) {
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
grpc_subchannel_call *call; grpc_subchannel_call *call;
@ -1079,7 +1080,7 @@ static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx,
/* need to recheck that another thread hasn't set the call */ /* need to recheck that another thread hasn't set the call */
call = GET_CALL(calld); call = GET_CALL(calld);
if (call == CANCELLED_CALL) { if (call == CANCELLED_CALL) {
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error)); exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error));
/* early out */ /* early out */
return; return;
@ -1090,11 +1091,11 @@ static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx,
return; return;
} }
/* if this is a cancellation, then we can raise our cancelled flag */ /* if this is a cancellation, then we can raise our cancelled flag */
if (op->cancel_error != GRPC_ERROR_NONE) { if (op->cancel_stream) {
if (!gpr_atm_rel_cas(&calld->subchannel_call, 0, if (!gpr_atm_rel_cas(&calld->subchannel_call, 0,
(gpr_atm)(uintptr_t)CANCELLED_CALL)) { (gpr_atm)(uintptr_t)CANCELLED_CALL)) {
/* recurse to retry */ /* recurse to retry */
start_transport_stream_op_locked_inner(exec_ctx, op, elem); start_transport_stream_op_batch_locked_inner(exec_ctx, op, elem);
/* early out */ /* early out */
return; return;
} else { } else {
@ -1103,27 +1104,29 @@ static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx,
cancelled before any ops are passed down (e.g., if the deadline cancelled before any ops are passed down (e.g., if the deadline
is in the past when the call starts), we can return the right is in the past when the call starts), we can return the right
error to the caller when the first op does get passed down. */ error to the caller when the first op does get passed down. */
calld->cancel_error = GRPC_ERROR_REF(op->cancel_error); calld->cancel_error =
GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error);
switch (calld->creation_phase) { switch (calld->creation_phase) {
case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING: case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
fail_locked(exec_ctx, calld, GRPC_ERROR_REF(op->cancel_error)); fail_locked(exec_ctx, calld,
GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
break; break;
case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL: case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
pick_subchannel_locked(exec_ctx, elem, NULL, 0, pick_subchannel_locked(
&calld->connected_subchannel, NULL, exec_ctx, elem, NULL, 0, &calld->connected_subchannel, NULL,
GRPC_ERROR_REF(op->cancel_error)); GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
break; break;
} }
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, GRPC_ERROR_REF(op->cancel_error)); exec_ctx, op,
GRPC_ERROR_REF(op->payload->cancel_stream.cancel_error));
/* early out */ /* early out */
return; return;
} }
} }
/* if we don't have a subchannel, try to get one */ /* if we don't have a subchannel, try to get one */
if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && if (calld->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING &&
calld->connected_subchannel == NULL && calld->connected_subchannel == NULL && op->send_initial_metadata) {
op->send_initial_metadata != NULL) {
calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL; calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL;
grpc_closure_init(&calld->next_step, subchannel_ready_locked, elem, grpc_closure_init(&calld->next_step, subchannel_ready_locked, elem,
grpc_combiner_scheduler(chand->combiner, true)); grpc_combiner_scheduler(chand->combiner, true));
@ -1131,10 +1134,11 @@ static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx,
/* If a subchannel is not available immediately, the polling entity from /* If a subchannel is not available immediately, the polling entity from
call_data should be provided to channel_data's interested_parties, so call_data should be provided to channel_data's interested_parties, so
that IO of the lb_policy and resolver could be done under it. */ that IO of the lb_policy and resolver could be done under it. */
if (pick_subchannel_locked(exec_ctx, elem, op->send_initial_metadata, if (pick_subchannel_locked(
op->send_initial_metadata_flags, exec_ctx, elem,
&calld->connected_subchannel, &calld->next_step, op->payload->send_initial_metadata.send_initial_metadata,
GRPC_ERROR_NONE)) { op->payload->send_initial_metadata.send_initial_metadata_flags,
&calld->connected_subchannel, &calld->next_step, GRPC_ERROR_NONE)) {
calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; calld->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel"); GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_subchannel");
} else { } else {
@ -1157,13 +1161,13 @@ static void start_transport_stream_op_locked_inner(grpc_exec_ctx *exec_ctx,
if (error != GRPC_ERROR_NONE) { if (error != GRPC_ERROR_NONE) {
subchannel_call = CANCELLED_CALL; subchannel_call = CANCELLED_CALL;
fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error)); fail_locked(exec_ctx, calld, GRPC_ERROR_REF(error));
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
} }
gpr_atm_rel_store(&calld->subchannel_call, gpr_atm_rel_store(&calld->subchannel_call,
(gpr_atm)(uintptr_t)subchannel_call); (gpr_atm)(uintptr_t)subchannel_call);
retry_waiting_locked(exec_ctx, calld); retry_waiting_locked(exec_ctx, calld);
/* recurse to retry */ /* recurse to retry */
start_transport_stream_op_locked_inner(exec_ctx, op, elem); start_transport_stream_op_batch_locked_inner(exec_ctx, op, elem);
/* early out */ /* early out */
return; return;
} }
@ -1191,15 +1195,16 @@ static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
GRPC_ERROR_REF(error)); GRPC_ERROR_REF(error));
} }
static void start_transport_stream_op_locked(grpc_exec_ctx *exec_ctx, void *arg, static void start_transport_stream_op_batch_locked(grpc_exec_ctx *exec_ctx,
grpc_error *error_ignored) { void *arg,
GPR_TIMER_BEGIN("start_transport_stream_op_locked", 0); grpc_error *error_ignored) {
GPR_TIMER_BEGIN("start_transport_stream_op_batch_locked", 0);
grpc_transport_stream_op *op = arg; grpc_transport_stream_op_batch *op = arg;
grpc_call_element *elem = op->handler_private.args[0]; grpc_call_element *elem = op->handler_private.extra_arg;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
GPR_ASSERT(op->on_complete != NULL); GPR_ASSERT(op->on_complete != NULL);
calld->original_on_complete = op->on_complete; calld->original_on_complete = op->on_complete;
grpc_closure_init(&calld->on_complete, on_complete, elem, grpc_closure_init(&calld->on_complete, on_complete, elem,
@ -1207,11 +1212,11 @@ static void start_transport_stream_op_locked(grpc_exec_ctx *exec_ctx, void *arg,
op->on_complete = &calld->on_complete; op->on_complete = &calld->on_complete;
} }
start_transport_stream_op_locked_inner(exec_ctx, op, elem); start_transport_stream_op_batch_locked_inner(exec_ctx, op, elem);
GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call,
"start_transport_stream_op"); "start_transport_stream_op_batch");
GPR_TIMER_END("start_transport_stream_op_locked", 0); GPR_TIMER_END("start_transport_stream_op_batch_locked", 0);
} }
/* The logic here is fairly complicated, due to (a) the fact that we /* The logic here is fairly complicated, due to (a) the fact that we
@ -1222,41 +1227,42 @@ static void start_transport_stream_op_locked(grpc_exec_ctx *exec_ctx, void *arg,
We use double-checked locking to initially see if initialization has been We use double-checked locking to initially see if initialization has been
performed. If it has not, we acquire the combiner and perform initialization. performed. If it has not, we acquire the combiner and perform initialization.
If it has, we proceed on the fast path. */ If it has, we proceed on the fast path. */
static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void cc_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
if (chand->deadline_checking_enabled) { if (chand->deadline_checking_enabled) {
grpc_deadline_state_client_start_transport_stream_op(exec_ctx, elem, op); grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem,
op);
} }
/* try to (atomically) get the call */ /* try to (atomically) get the call */
grpc_subchannel_call *call = GET_CALL(calld); grpc_subchannel_call *call = GET_CALL(calld);
GPR_TIMER_BEGIN("cc_start_transport_stream_op", 0); GPR_TIMER_BEGIN("cc_start_transport_stream_op_batch", 0);
if (call == CANCELLED_CALL) { if (call == CANCELLED_CALL) {
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error)); exec_ctx, op, GRPC_ERROR_REF(calld->cancel_error));
GPR_TIMER_END("cc_start_transport_stream_op", 0); GPR_TIMER_END("cc_start_transport_stream_op_batch", 0);
/* early out */ /* early out */
return; return;
} }
if (call != NULL) { if (call != NULL) {
grpc_subchannel_call_process_op(exec_ctx, call, op); grpc_subchannel_call_process_op(exec_ctx, call, op);
GPR_TIMER_END("cc_start_transport_stream_op", 0); GPR_TIMER_END("cc_start_transport_stream_op_batch", 0);
/* early out */ /* early out */
return; return;
} }
/* we failed; lock and figure out what to do */ /* we failed; lock and figure out what to do */
GRPC_CALL_STACK_REF(calld->owning_call, "start_transport_stream_op"); GRPC_CALL_STACK_REF(calld->owning_call, "start_transport_stream_op_batch");
op->handler_private.args[0] = elem; op->handler_private.extra_arg = elem;
grpc_closure_sched( grpc_closure_sched(
exec_ctx, exec_ctx,
grpc_closure_init(&op->handler_private.closure, grpc_closure_init(&op->handler_private.closure,
start_transport_stream_op_locked, op, start_transport_stream_op_batch_locked, op,
grpc_combiner_scheduler(chand->combiner, false)), grpc_combiner_scheduler(chand->combiner, false)),
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
GPR_TIMER_END("cc_start_transport_stream_op", 0); GPR_TIMER_END("cc_start_transport_stream_op_batch", 0);
} }
/* Constructor for call_data */ /* Constructor for call_data */
@ -1321,7 +1327,7 @@ static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx,
*/ */
const grpc_channel_filter grpc_client_channel_filter = { const grpc_channel_filter grpc_client_channel_filter = {
cc_start_transport_stream_op, cc_start_transport_stream_op_batch,
cc_start_transport_op, cc_start_transport_op,
sizeof(call_data), sizeof(call_data),
cc_init_call_elem, cc_init_call_elem,

@ -748,11 +748,11 @@ char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx,
void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx,
grpc_subchannel_call *call, grpc_subchannel_call *call,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("grpc_subchannel_call_process_op", 0); GPR_TIMER_BEGIN("grpc_subchannel_call_process_op", 0);
grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call);
grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0);
top_elem->filter->start_transport_stream_op(exec_ctx, top_elem, op); top_elem->filter->start_transport_stream_op_batch(exec_ctx, top_elem, op);
GPR_TIMER_END("grpc_subchannel_call_process_op", 0); GPR_TIMER_END("grpc_subchannel_call_process_op", 0);
} }

@ -157,7 +157,7 @@ grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel(
/** continue processing a transport op */ /** continue processing a transport op */
void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx,
grpc_subchannel_call *subchannel_call, grpc_subchannel_call *subchannel_call,
grpc_transport_stream_op *op); grpc_transport_stream_op_batch *op);
/** continue querying for peer */ /** continue querying for peer */
char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx,

@ -136,7 +136,7 @@ static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
// Inject our own on_complete callback into op. // Inject our own on_complete callback into op.
static void inject_on_complete_cb(grpc_deadline_state* deadline_state, static void inject_on_complete_cb(grpc_deadline_state* deadline_state,
grpc_transport_stream_op* op) { grpc_transport_stream_op_batch* op) {
deadline_state->next_on_complete = op->on_complete; deadline_state->next_on_complete = op->on_complete;
grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state, grpc_closure_init(&deadline_state->on_complete, on_complete, deadline_state,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
@ -198,16 +198,16 @@ void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
start_timer_if_needed(exec_ctx, elem, new_deadline); start_timer_if_needed(exec_ctx, elem, new_deadline);
} }
void grpc_deadline_state_client_start_transport_stream_op( void grpc_deadline_state_client_start_transport_stream_op_batch(
grpc_exec_ctx* exec_ctx, grpc_call_element* elem, grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
grpc_transport_stream_op* op) { grpc_transport_stream_op_batch* op) {
grpc_deadline_state* deadline_state = elem->call_data; grpc_deadline_state* deadline_state = elem->call_data;
if (op->cancel_error != GRPC_ERROR_NONE) { if (op->cancel_stream) {
cancel_timer_if_needed(exec_ctx, deadline_state); cancel_timer_if_needed(exec_ctx, deadline_state);
} else { } else {
// Make sure we know when the call is complete, so that we can cancel // Make sure we know when the call is complete, so that we can cancel
// the timer. // the timer.
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
inject_on_complete_cb(deadline_state, op); inject_on_complete_cb(deadline_state, op);
} }
} }
@ -263,10 +263,11 @@ static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
} }
// Method for starting a call op for client filter. // Method for starting a call op for client filter.
static void client_start_transport_stream_op(grpc_exec_ctx* exec_ctx, static void client_start_transport_stream_op_batch(
grpc_call_element* elem, grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
grpc_transport_stream_op* op) { grpc_transport_stream_op_batch* op) {
grpc_deadline_state_client_start_transport_stream_op(exec_ctx, elem, op); grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem,
op);
// Chain to next filter. // Chain to next filter.
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
} }
@ -284,30 +285,33 @@ static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* arg,
} }
// Method for starting a call op for server filter. // Method for starting a call op for server filter.
static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx, static void server_start_transport_stream_op_batch(
grpc_call_element* elem, grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
grpc_transport_stream_op* op) { grpc_transport_stream_op_batch* op) {
server_call_data* calld = elem->call_data; server_call_data* calld = elem->call_data;
if (op->cancel_error != GRPC_ERROR_NONE) { if (op->cancel_stream) {
cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state); cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state);
} else { } else {
// If we're receiving initial metadata, we need to get the deadline // If we're receiving initial metadata, we need to get the deadline
// from the recv_initial_metadata_ready callback. So we inject our // from the recv_initial_metadata_ready callback. So we inject our
// own callback into that hook. // own callback into that hook.
if (op->recv_initial_metadata_ready != NULL) { if (op->recv_initial_metadata) {
calld->next_recv_initial_metadata_ready = op->recv_initial_metadata_ready; calld->next_recv_initial_metadata_ready =
calld->recv_initial_metadata = op->recv_initial_metadata; op->payload->recv_initial_metadata.recv_initial_metadata_ready;
calld->recv_initial_metadata =
op->payload->recv_initial_metadata.recv_initial_metadata;
grpc_closure_init(&calld->recv_initial_metadata_ready, grpc_closure_init(&calld->recv_initial_metadata_ready,
recv_initial_metadata_ready, elem, recv_initial_metadata_ready, elem,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
op->recv_initial_metadata_ready = &calld->recv_initial_metadata_ready; op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->recv_initial_metadata_ready;
} }
// Make sure we know when the call is complete, so that we can cancel // Make sure we know when the call is complete, so that we can cancel
// the timer. // the timer.
// Note that we trigger this on recv_trailing_metadata, even though // Note that we trigger this on recv_trailing_metadata, even though
// the client never sends trailing metadata, because this is the // the client never sends trailing metadata, because this is the
// hook that tells us when the call is complete on the server side. // hook that tells us when the call is complete on the server side.
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
inject_on_complete_cb(&calld->base.deadline_state, op); inject_on_complete_cb(&calld->base.deadline_state, op);
} }
} }
@ -316,7 +320,7 @@ static void server_start_transport_stream_op(grpc_exec_ctx* exec_ctx,
} }
const grpc_channel_filter grpc_client_deadline_filter = { const grpc_channel_filter grpc_client_deadline_filter = {
client_start_transport_stream_op, client_start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(base_call_data), sizeof(base_call_data),
init_call_elem, init_call_elem,
@ -331,7 +335,7 @@ const grpc_channel_filter grpc_client_deadline_filter = {
}; };
const grpc_channel_filter grpc_server_deadline_filter = { const grpc_channel_filter grpc_server_deadline_filter = {
server_start_transport_stream_op, server_start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(server_call_data), sizeof(server_call_data),
init_call_elem, init_call_elem,

@ -83,15 +83,15 @@ void grpc_deadline_state_start(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
gpr_timespec new_deadline); gpr_timespec new_deadline);
// To be called from the client-side filter's start_transport_stream_op() // To be called from the client-side filter's start_transport_stream_op_batch()
// method. Ensures that the deadline timer is cancelled when the call // method. Ensures that the deadline timer is cancelled when the call
// is completed. // is completed.
// //
// Note: It is the caller's responsibility to chain to the next filter if // Note: It is the caller's responsibility to chain to the next filter if
// necessary after this function returns. // necessary after this function returns.
void grpc_deadline_state_client_start_transport_stream_op( void grpc_deadline_state_client_start_transport_stream_op_batch(
grpc_exec_ctx* exec_ctx, grpc_call_element* elem, grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
grpc_transport_stream_op* op); grpc_transport_stream_op_batch* op);
// Should deadline checking be performed (according to channel args) // Should deadline checking be performed (according to channel args)
bool grpc_deadline_checking_enabled(const grpc_channel_args* args); bool grpc_deadline_checking_enabled(const grpc_channel_args* args);

@ -63,7 +63,7 @@ typedef struct call_data {
uint8_t *payload_bytes; uint8_t *payload_bytes;
/* Vars to read data off of send_message */ /* Vars to read data off of send_message */
grpc_transport_stream_op send_op; grpc_transport_stream_op_batch *send_op;
uint32_t send_length; uint32_t send_length;
uint32_t send_flags; uint32_t send_flags;
grpc_slice incoming_slice; grpc_slice incoming_slice;
@ -219,9 +219,9 @@ static void continue_send_message(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem) { grpc_call_element *elem) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
uint8_t *wrptr = calld->payload_bytes; uint8_t *wrptr = calld->payload_bytes;
while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message, while (grpc_byte_stream_next(
&calld->incoming_slice, ~(size_t)0, exec_ctx, calld->send_op->payload->send_message.send_message,
&calld->got_slice)) { &calld->incoming_slice, ~(size_t)0, &calld->got_slice)) {
memcpy(wrptr, GRPC_SLICE_START_PTR(calld->incoming_slice), memcpy(wrptr, GRPC_SLICE_START_PTR(calld->incoming_slice),
GRPC_SLICE_LENGTH(calld->incoming_slice)); GRPC_SLICE_LENGTH(calld->incoming_slice));
wrptr += GRPC_SLICE_LENGTH(calld->incoming_slice); wrptr += GRPC_SLICE_LENGTH(calld->incoming_slice);
@ -242,10 +242,11 @@ static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
/* Pass down the original send_message op that was blocked.*/ /* Pass down the original send_message op that was blocked.*/
grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
calld->send_flags); calld->send_flags);
calld->send_op.send_message = &calld->replacement_stream.base; calld->send_op->payload->send_message.send_message =
calld->post_send = calld->send_op.on_complete; &calld->replacement_stream.base;
calld->send_op.on_complete = &calld->send_done; calld->post_send = calld->send_op->on_complete;
grpc_call_next_op(exec_ctx, elem, &calld->send_op); calld->send_op->on_complete = &calld->send_done;
grpc_call_next_op(exec_ctx, elem, calld->send_op);
} else { } else {
continue_send_message(exec_ctx, elem); continue_send_message(exec_ctx, elem);
} }
@ -253,29 +254,30 @@ static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx, static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
/* grab pointers to our data from the call element */ /* grab pointers to our data from the call element */
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
grpc_error *error; grpc_error *error;
if (op->send_initial_metadata != NULL) { if (op->send_initial_metadata) {
/* Decide which HTTP VERB to use. We use GET if the request is marked /* Decide which HTTP VERB to use. We use GET if the request is marked
cacheable, and the operation contains both initial metadata and send cacheable, and the operation contains both initial metadata and send
message, and the payload is below the size threshold, and all the data message, and the payload is below the size threshold, and all the data
for this request is immediately available. */ for this request is immediately available. */
grpc_mdelem method = GRPC_MDELEM_METHOD_POST; grpc_mdelem method = GRPC_MDELEM_METHOD_POST;
if ((op->send_initial_metadata_flags & if (op->send_message &&
(op->payload->send_initial_metadata.send_initial_metadata_flags &
GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) && GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) &&
op->send_message != NULL && op->payload->send_message.send_message->length <
op->send_message->length < channeld->max_payload_size_for_get) { channeld->max_payload_size_for_get) {
method = GRPC_MDELEM_METHOD_GET; method = GRPC_MDELEM_METHOD_GET;
/* The following write to calld->send_message_blocked isn't racy with /* The following write to calld->send_message_blocked isn't racy with
reads in hc_start_transport_op (which deals with SEND_MESSAGE ops) because reads in hc_start_transport_op (which deals with SEND_MESSAGE ops) because
being here means ops->send_message is not NULL, which is primarily being here means ops->send_message is not NULL, which is primarily
guarding the read there. */ guarding the read there. */
calld->send_message_blocked = true; calld->send_message_blocked = true;
} else if (op->send_initial_metadata_flags & } else if (op->payload->send_initial_metadata.send_initial_metadata_flags &
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) {
method = GRPC_MDELEM_METHOD_PUT; method = GRPC_MDELEM_METHOD_PUT;
} }
@ -283,12 +285,13 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
/* Attempt to read the data from send_message and create a header field. */ /* Attempt to read the data from send_message and create a header field. */
if (grpc_mdelem_eq(method, GRPC_MDELEM_METHOD_GET)) { if (grpc_mdelem_eq(method, GRPC_MDELEM_METHOD_GET)) {
/* allocate memory to hold the entire payload */ /* allocate memory to hold the entire payload */
calld->payload_bytes = gpr_malloc(op->send_message->length); calld->payload_bytes =
gpr_malloc(op->payload->send_message.send_message->length);
/* read slices of send_message and copy into payload_bytes */ /* read slices of send_message and copy into payload_bytes */
calld->send_op = *op; calld->send_op = op;
calld->send_length = op->send_message->length; calld->send_length = op->payload->send_message.send_message->length;
calld->send_flags = op->send_message->flags; calld->send_flags = op->payload->send_message.send_message->flags;
continue_send_message(exec_ctx, elem); continue_send_message(exec_ctx, elem);
if (calld->send_message_blocked == false) { if (calld->send_message_blocked == false) {
@ -299,13 +302,15 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
const unsigned char k_query_separator = '?'; const unsigned char k_query_separator = '?';
grpc_slice path_slice = grpc_slice path_slice =
GRPC_MDVALUE(op->send_initial_metadata->idx.named.path->md); GRPC_MDVALUE(op->payload->send_initial_metadata
.send_initial_metadata->idx.named.path->md);
/* sum up individual component's lengths and allocate enough memory to /* sum up individual component's lengths and allocate enough memory to
* hold combined path+query */ * hold combined path+query */
size_t estimated_len = GRPC_SLICE_LENGTH(path_slice); size_t estimated_len = GRPC_SLICE_LENGTH(path_slice);
estimated_len++; /* for the '?' */ estimated_len++; /* for the '?' */
estimated_len += grpc_base64_estimate_encoded_size( estimated_len += grpc_base64_estimate_encoded_size(
op->send_message->length, k_url_safe, k_multi_line); op->payload->send_message.send_message->length, k_url_safe,
k_multi_line);
estimated_len += 1; /* for the trailing 0 */ estimated_len += 1; /* for the trailing 0 */
grpc_slice path_with_query_slice = grpc_slice_malloc(estimated_len); grpc_slice path_with_query_slice = grpc_slice_malloc(estimated_len);
@ -320,8 +325,8 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
write_ptr++; /* for the '?' */ write_ptr++; /* for the '?' */
grpc_base64_encode_core((char *)write_ptr, calld->payload_bytes, grpc_base64_encode_core((char *)write_ptr, calld->payload_bytes,
op->send_message->length, k_url_safe, op->payload->send_message.send_message->length,
k_multi_line); k_url_safe, k_multi_line);
/* remove trailing unused memory and add trailing 0 to terminate string /* remove trailing unused memory and add trailing 0 to terminate string
*/ */
@ -335,14 +340,15 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
/* substitute previous path with the new path+query */ /* substitute previous path with the new path+query */
grpc_mdelem mdelem_path_and_query = grpc_mdelem_from_slices( grpc_mdelem mdelem_path_and_query = grpc_mdelem_from_slices(
exec_ctx, GRPC_MDSTR_PATH, path_with_query_slice); exec_ctx, GRPC_MDSTR_PATH, path_with_query_slice);
grpc_metadata_batch *b = op->send_initial_metadata; grpc_metadata_batch *b =
op->payload->send_initial_metadata.send_initial_metadata;
error = grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path, error = grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path,
mdelem_path_and_query); mdelem_path_and_query);
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
calld->on_complete = op->on_complete; calld->on_complete = op->on_complete;
op->on_complete = &calld->hc_on_complete; op->on_complete = &calld->hc_on_complete;
op->send_message = NULL; op->send_message = false;
grpc_slice_unref_internal(exec_ctx, path_with_query_slice); grpc_slice_unref_internal(exec_ctx, path_with_query_slice);
} else { } else {
/* Not all data is available. Fall back to POST. */ /* Not all data is available. Fall back to POST. */
@ -353,47 +359,60 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
} }
} }
remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_METHOD); remove_if_present(exec_ctx,
remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_SCHEME); op->payload->send_initial_metadata.send_initial_metadata,
remove_if_present(exec_ctx, op->send_initial_metadata, GRPC_BATCH_TE); GRPC_BATCH_METHOD);
remove_if_present(exec_ctx, op->send_initial_metadata, remove_if_present(exec_ctx,
op->payload->send_initial_metadata.send_initial_metadata,
GRPC_BATCH_SCHEME);
remove_if_present(exec_ctx,
op->payload->send_initial_metadata.send_initial_metadata,
GRPC_BATCH_TE);
remove_if_present(exec_ctx,
op->payload->send_initial_metadata.send_initial_metadata,
GRPC_BATCH_CONTENT_TYPE); GRPC_BATCH_CONTENT_TYPE);
remove_if_present(exec_ctx, op->send_initial_metadata, remove_if_present(exec_ctx,
op->payload->send_initial_metadata.send_initial_metadata,
GRPC_BATCH_USER_AGENT); GRPC_BATCH_USER_AGENT);
/* Send : prefixed headers, which have to be before any application /* Send : prefixed headers, which have to be before any application
layer headers. */ layer headers. */
error = grpc_metadata_batch_add_head(exec_ctx, op->send_initial_metadata, error = grpc_metadata_batch_add_head(
&calld->method, method); exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
&calld->method, method);
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
error = error = grpc_metadata_batch_add_head(
grpc_metadata_batch_add_head(exec_ctx, op->send_initial_metadata, exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
&calld->scheme, channeld->static_scheme); &calld->scheme, channeld->static_scheme);
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
error = grpc_metadata_batch_add_tail(exec_ctx, op->send_initial_metadata, error = grpc_metadata_batch_add_tail(
&calld->te_trailers, exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
GRPC_MDELEM_TE_TRAILERS); &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS);
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
error = grpc_metadata_batch_add_tail( error = grpc_metadata_batch_add_tail(
exec_ctx, op->send_initial_metadata, &calld->content_type, exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC);
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
error = grpc_metadata_batch_add_tail(exec_ctx, op->send_initial_metadata, error = grpc_metadata_batch_add_tail(
&calld->user_agent, exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
GRPC_MDELEM_REF(channeld->user_agent)); &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent));
if (error != GRPC_ERROR_NONE) return error; if (error != GRPC_ERROR_NONE) return error;
} }
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
/* substitute our callback for the higher callback */ /* substitute our callback for the higher callback */
calld->recv_initial_metadata = op->recv_initial_metadata; calld->recv_initial_metadata =
calld->on_done_recv_initial_metadata = op->recv_initial_metadata_ready; op->payload->recv_initial_metadata.recv_initial_metadata;
op->recv_initial_metadata_ready = &calld->hc_on_recv_initial_metadata; calld->on_done_recv_initial_metadata =
op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->hc_on_recv_initial_metadata;
} }
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
/* substitute our callback for the higher callback */ /* substitute our callback for the higher callback */
calld->recv_trailing_metadata = op->recv_trailing_metadata; calld->recv_trailing_metadata =
op->payload->recv_trailing_metadata.recv_trailing_metadata;
calld->on_done_recv_trailing_metadata = op->on_complete; calld->on_done_recv_trailing_metadata = op->on_complete;
op->on_complete = &calld->hc_on_recv_trailing_metadata; op->on_complete = &calld->hc_on_recv_trailing_metadata;
} }
@ -403,17 +422,17 @@ static grpc_error *hc_mutate_op(grpc_exec_ctx *exec_ctx,
static void hc_start_transport_op(grpc_exec_ctx *exec_ctx, static void hc_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("hc_start_transport_op", 0); GPR_TIMER_BEGIN("hc_start_transport_op", 0);
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
grpc_error *error = hc_mutate_op(exec_ctx, elem, op); grpc_error *error = hc_mutate_op(exec_ctx, elem, op);
if (error != GRPC_ERROR_NONE) { if (error != GRPC_ERROR_NONE) {
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
} else { } else {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->send_message != NULL && calld->send_message_blocked) { if (op->send_message && calld->send_message_blocked) {
/* Don't forward the op. send_message contains slices that aren't ready /* Don't forward the op. send_message contains slices that aren't ready
yet. The call will be forwarded by the op_complete of slice read call. yet. The call will be forwarded by the op_complete of slice read call.
*/ */
} else { } else {
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);

@ -62,7 +62,7 @@ typedef struct call_data {
/** If true, contents of \a compression_algorithm are authoritative */ /** If true, contents of \a compression_algorithm are authoritative */
int has_compression_algorithm; int has_compression_algorithm;
grpc_transport_stream_op *send_op; grpc_transport_stream_op_batch *send_op;
uint32_t send_length; uint32_t send_length;
uint32_t send_flags; uint32_t send_flags;
grpc_slice incoming_slice; grpc_slice incoming_slice;
@ -210,7 +210,8 @@ static void finish_send_message(grpc_exec_ctx *exec_ctx,
grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices,
calld->send_flags); calld->send_flags);
calld->send_op->send_message = &calld->replacement_stream.base; calld->send_op->payload->send_message.send_message =
&calld->replacement_stream.base;
calld->post_send = calld->send_op->on_complete; calld->post_send = calld->send_op->on_complete;
calld->send_op->on_complete = &calld->send_done; calld->send_op->on_complete = &calld->send_done;
@ -231,9 +232,9 @@ static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
static void continue_send_message(grpc_exec_ctx *exec_ctx, static void continue_send_message(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem) { grpc_call_element *elem) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
while (grpc_byte_stream_next(exec_ctx, calld->send_op->send_message, while (grpc_byte_stream_next(
&calld->incoming_slice, ~(size_t)0, exec_ctx, calld->send_op->payload->send_message.send_message,
&calld->got_slice)) { &calld->incoming_slice, ~(size_t)0, &calld->got_slice)) {
grpc_slice_buffer_add(&calld->slices, calld->incoming_slice); grpc_slice_buffer_add(&calld->slices, calld->incoming_slice);
if (calld->send_length == calld->slices.length) { if (calld->send_length == calld->slices.length) {
finish_send_message(exec_ctx, elem); finish_send_message(exec_ctx, elem);
@ -242,33 +243,34 @@ static void continue_send_message(grpc_exec_ctx *exec_ctx,
} }
} }
static void compress_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void compress_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0); GPR_TIMER_BEGIN("compress_start_transport_stream_op_batch", 0);
if (op->send_initial_metadata) { if (op->send_initial_metadata) {
grpc_error *error = process_send_initial_metadata( grpc_error *error = process_send_initial_metadata(
exec_ctx, elem, op->send_initial_metadata); exec_ctx, elem,
op->payload->send_initial_metadata.send_initial_metadata);
if (error != GRPC_ERROR_NONE) { if (error != GRPC_ERROR_NONE) {
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
return; return;
} }
} }
if (op->send_message != NULL && if (op->send_message &&
!skip_compression(elem, op->send_message->flags)) { !skip_compression(elem, op->payload->send_message.send_message->flags)) {
calld->send_op = op; calld->send_op = op;
calld->send_length = op->send_message->length; calld->send_length = op->payload->send_message.send_message->length;
calld->send_flags = op->send_message->flags; calld->send_flags = op->payload->send_message.send_message->flags;
continue_send_message(exec_ctx, elem); continue_send_message(exec_ctx, elem);
} else { } else {
/* pass control down the stack */ /* pass control down the stack */
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
} }
GPR_TIMER_END("compress_start_transport_stream_op", 0); GPR_TIMER_END("compress_start_transport_stream_op_batch", 0);
} }
/* Constructor for call_data */ /* Constructor for call_data */
@ -337,7 +339,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem) {} grpc_channel_element *elem) {}
const grpc_channel_filter grpc_compress_filter = { const grpc_channel_filter grpc_compress_filter = {
compress_start_transport_stream_op, compress_start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -58,8 +58,7 @@ typedef struct call_data {
bool payload_bin_delivered; bool payload_bin_delivered;
grpc_metadata_batch *recv_initial_metadata; grpc_metadata_batch *recv_initial_metadata;
bool *recv_idempotent_request; uint32_t *recv_initial_metadata_flags;
bool *recv_cacheable_request;
/** Closure to call when finished with the hs_on_recv hook */ /** Closure to call when finished with the hs_on_recv hook */
grpc_closure *on_done_recv; grpc_closure *on_done_recv;
/** Closure to call when we retrieve read message from the path URI /** Closure to call when we retrieve read message from the path URI
@ -116,14 +115,21 @@ static grpc_error *server_filter_incoming_metadata(grpc_exec_ctx *exec_ctx,
if (b->idx.named.method != NULL) { if (b->idx.named.method != NULL) {
if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) { if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) {
*calld->recv_idempotent_request = false; *calld->recv_initial_metadata_flags &=
*calld->recv_cacheable_request = false; ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST |
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST);
} else if (grpc_mdelem_eq(b->idx.named.method->md, } else if (grpc_mdelem_eq(b->idx.named.method->md,
GRPC_MDELEM_METHOD_PUT)) { GRPC_MDELEM_METHOD_PUT)) {
*calld->recv_idempotent_request = true; *calld->recv_initial_metadata_flags &=
~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
*calld->recv_initial_metadata_flags |=
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
} else if (grpc_mdelem_eq(b->idx.named.method->md, } else if (grpc_mdelem_eq(b->idx.named.method->md,
GRPC_MDELEM_METHOD_GET)) { GRPC_MDELEM_METHOD_GET)) {
*calld->recv_cacheable_request = true; *calld->recv_initial_metadata_flags |=
GRPC_INITIAL_METADATA_CACHEABLE_REQUEST;
*calld->recv_initial_metadata_flags &=
~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST;
} else { } else {
add_error(error_name, &error, add_error(error_name, &error,
grpc_attach_md_to_error( grpc_attach_md_to_error(
@ -206,7 +212,8 @@ static grpc_error *server_filter_incoming_metadata(grpc_exec_ctx *exec_ctx,
grpc_error_set_str( grpc_error_set_str(
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"),
GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path"))); GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path")));
} else if (*calld->recv_cacheable_request == true) { } else if (*calld->recv_initial_metadata_flags &
GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) {
/* We have a cacheable request made with GET verb. The path contains the /* We have a cacheable request made with GET verb. The path contains the
* query parameter which is base64 encoded request payload. */ * query parameter which is base64 encoded request payload. */
const char k_query_separator = '?'; const char k_query_separator = '?';
@ -311,45 +318,53 @@ static void hs_recv_message_ready(grpc_exec_ctx *exec_ctx, void *user_data,
} }
static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
/* grab pointers to our data from the call element */ /* grab pointers to our data from the call element */
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->send_initial_metadata != NULL) { if (op->send_initial_metadata) {
grpc_error *error = GRPC_ERROR_NONE; grpc_error *error = GRPC_ERROR_NONE;
static const char *error_name = "Failed sending initial metadata"; static const char *error_name = "Failed sending initial metadata";
add_error(error_name, &error, grpc_metadata_batch_add_head( add_error(
exec_ctx, op->send_initial_metadata, error_name, &error,
&calld->status, GRPC_MDELEM_STATUS_200)); grpc_metadata_batch_add_head(
add_error(error_name, &error, exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
grpc_metadata_batch_add_tail( &calld->status, GRPC_MDELEM_STATUS_200));
exec_ctx, op->send_initial_metadata, &calld->content_type, add_error(
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)); error_name, &error,
grpc_metadata_batch_add_tail(
exec_ctx, op->payload->send_initial_metadata.send_initial_metadata,
&calld->content_type,
GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC));
add_error(error_name, &error, add_error(error_name, &error,
server_filter_outgoing_metadata(exec_ctx, elem, server_filter_outgoing_metadata(
op->send_initial_metadata)); exec_ctx, elem,
op->payload->send_initial_metadata.send_initial_metadata));
if (error != GRPC_ERROR_NONE) { if (error != GRPC_ERROR_NONE) {
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
return; return;
} }
} }
if (op->recv_initial_metadata) { if (op->recv_initial_metadata) {
/* substitute our callback for the higher callback */ /* substitute our callback for the higher callback */
GPR_ASSERT(op->recv_idempotent_request != NULL); GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != NULL);
GPR_ASSERT(op->recv_cacheable_request != NULL); calld->recv_initial_metadata =
calld->recv_initial_metadata = op->recv_initial_metadata; op->payload->recv_initial_metadata.recv_initial_metadata;
calld->recv_idempotent_request = op->recv_idempotent_request; calld->recv_initial_metadata_flags =
calld->recv_cacheable_request = op->recv_cacheable_request; op->payload->recv_initial_metadata.recv_flags;
calld->on_done_recv = op->recv_initial_metadata_ready; calld->on_done_recv =
op->recv_initial_metadata_ready = &calld->hs_on_recv; op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->hs_on_recv;
} }
if (op->recv_message) { if (op->recv_message) {
calld->recv_message_ready = op->recv_message_ready; calld->recv_message_ready = op->payload->recv_message.recv_message_ready;
calld->pp_recv_message = op->recv_message; calld->pp_recv_message = op->payload->recv_message.recv_message;
if (op->recv_message_ready) { if (op->payload->recv_message.recv_message_ready) {
op->recv_message_ready = &calld->hs_recv_message_ready; op->payload->recv_message.recv_message_ready =
&calld->hs_recv_message_ready;
} }
if (op->on_complete) { if (op->on_complete) {
calld->on_complete = op->on_complete; calld->on_complete = op->on_complete;
@ -359,9 +374,10 @@ static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
if (op->send_trailing_metadata) { if (op->send_trailing_metadata) {
grpc_error *error = server_filter_outgoing_metadata( grpc_error *error = server_filter_outgoing_metadata(
exec_ctx, elem, op->send_trailing_metadata); exec_ctx, elem,
op->payload->send_trailing_metadata.send_trailing_metadata);
if (error != GRPC_ERROR_NONE) { if (error != GRPC_ERROR_NONE) {
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
return; return;
} }
} }
@ -369,7 +385,7 @@ static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
static void hs_start_transport_op(grpc_exec_ctx *exec_ctx, static void hs_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
GPR_TIMER_BEGIN("hs_start_transport_op", 0); GPR_TIMER_BEGIN("hs_start_transport_op", 0);
hs_mutate_op(exec_ctx, elem, op); hs_mutate_op(exec_ctx, elem, op);

@ -183,25 +183,28 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
*/ */
} }
static void lr_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void lr_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("lr_start_transport_stream_op", 0); GPR_TIMER_BEGIN("lr_start_transport_stream_op_batch", 0);
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_initial_metadata) { if (op->recv_initial_metadata) {
calld->recv_initial_metadata = op->recv_initial_metadata; calld->recv_initial_metadata =
op->payload->recv_initial_metadata.recv_initial_metadata;
/* substitute our callback for the higher callback */ /* substitute our callback for the higher callback */
calld->ops_recv_initial_metadata_ready = op->recv_initial_metadata_ready; calld->ops_recv_initial_metadata_ready =
op->recv_initial_metadata_ready = &calld->on_initial_md_ready; op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->on_initial_md_ready;
} }
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
GPR_TIMER_END("lr_start_transport_stream_op", 0); GPR_TIMER_END("lr_start_transport_stream_op_batch", 0);
} }
const grpc_channel_filter grpc_load_reporting_filter = { const grpc_channel_filter grpc_load_reporting_filter = {
lr_start_transport_stream_op, lr_start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -134,21 +134,23 @@ static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data,
gpr_free(message_string); gpr_free(message_string);
} }
// Invoke the next callback. // Invoke the next callback.
grpc_closure_sched(exec_ctx, calld->next_recv_message_ready, error); grpc_closure_run(exec_ctx, calld->next_recv_message_ready, error);
} }
// Start transport stream op. // Start transport stream op.
static void start_transport_stream_op(grpc_exec_ctx* exec_ctx, static void start_transport_stream_op_batch(
grpc_call_element* elem, grpc_exec_ctx* exec_ctx, grpc_call_element* elem,
grpc_transport_stream_op* op) { grpc_transport_stream_op_batch* op) {
call_data* calld = elem->call_data; call_data* calld = elem->call_data;
// Check max send message size. // Check max send message size.
if (op->send_message != NULL && calld->max_send_size >= 0 && if (op->send_message && calld->max_send_size >= 0 &&
op->send_message->length > (size_t)calld->max_send_size) { op->payload->send_message.send_message->length >
(size_t)calld->max_send_size) {
char* message_string; char* message_string;
gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)", gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)",
op->send_message->length, calld->max_send_size); op->payload->send_message.send_message->length,
grpc_transport_stream_op_finish_with_failure( calld->max_send_size);
grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, exec_ctx, op,
grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string),
GRPC_ERROR_INT_GRPC_STATUS, GRPC_ERROR_INT_GRPC_STATUS,
@ -157,10 +159,11 @@ static void start_transport_stream_op(grpc_exec_ctx* exec_ctx,
return; return;
} }
// Inject callback for receiving a message. // Inject callback for receiving a message.
if (op->recv_message_ready != NULL) { if (op->recv_message) {
calld->next_recv_message_ready = op->recv_message_ready; calld->next_recv_message_ready =
calld->recv_message = op->recv_message; op->payload->recv_message.recv_message_ready;
op->recv_message_ready = &calld->recv_message_ready; calld->recv_message = op->payload->recv_message.recv_message;
op->payload->recv_message.recv_message_ready = &calld->recv_message_ready;
} }
// Chain to the next filter. // Chain to the next filter.
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
@ -280,7 +283,7 @@ static void destroy_channel_elem(grpc_exec_ctx* exec_ctx,
} }
const grpc_channel_filter grpc_message_size_filter = { const grpc_channel_filter grpc_message_size_filter = {
start_transport_stream_op, start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -1140,20 +1140,23 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
grpc_error *error_ignored) { grpc_error *error_ignored) {
GPR_TIMER_BEGIN("perform_stream_op_locked", 0); GPR_TIMER_BEGIN("perform_stream_op_locked", 0);
grpc_transport_stream_op *op = stream_op; grpc_transport_stream_op_batch *op = stream_op;
grpc_chttp2_transport *t = op->handler_private.args[0]; grpc_chttp2_stream *s = op->handler_private.extra_arg;
grpc_chttp2_stream *s = op->handler_private.args[1]; grpc_transport_stream_op_batch_payload *op_payload = op->payload;
grpc_chttp2_transport *t = s->t;
if (grpc_http_trace) { if (grpc_http_trace) {
char *str = grpc_transport_stream_op_string(op); char *str = grpc_transport_stream_op_batch_string(op);
gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str, gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str,
op->on_complete); op->on_complete);
gpr_free(str); gpr_free(str);
if (op->send_initial_metadata) { if (op->send_initial_metadata) {
log_metadata(op->send_initial_metadata, s->id, t->is_client, true); log_metadata(op_payload->send_initial_metadata.send_initial_metadata,
s->id, t->is_client, true);
} }
if (op->send_trailing_metadata) { if (op->send_trailing_metadata) {
log_metadata(op->send_trailing_metadata, s->id, t->is_client, false); log_metadata(op_payload->send_trailing_metadata.send_trailing_metadata,
s->id, t->is_client, false);
} }
} }
@ -1168,23 +1171,25 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT; on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
on_complete->error_data.error = GRPC_ERROR_NONE; on_complete->error_data.error = GRPC_ERROR_NONE;
if (op->collect_stats != NULL) { if (op->collect_stats) {
GPR_ASSERT(s->collecting_stats == NULL); GPR_ASSERT(s->collecting_stats == NULL);
s->collecting_stats = op->collect_stats; s->collecting_stats = op_payload->collect_stats.collect_stats;
on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT; on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT;
} }
if (op->cancel_error != GRPC_ERROR_NONE) { if (op->cancel_stream) {
grpc_chttp2_cancel_stream(exec_ctx, t, s, op->cancel_error); grpc_chttp2_cancel_stream(exec_ctx, t, s,
op_payload->cancel_stream.cancel_error);
} }
if (op->send_initial_metadata != NULL) { if (op->send_initial_metadata) {
GPR_ASSERT(s->send_initial_metadata_finished == NULL); GPR_ASSERT(s->send_initial_metadata_finished == NULL);
on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
s->send_initial_metadata_finished = add_closure_barrier(on_complete); s->send_initial_metadata_finished = add_closure_barrier(on_complete);
s->send_initial_metadata = op->send_initial_metadata; s->send_initial_metadata =
op_payload->send_initial_metadata.send_initial_metadata;
const size_t metadata_size = const size_t metadata_size =
grpc_metadata_batch_size(op->send_initial_metadata); grpc_metadata_batch_size(s->send_initial_metadata);
const size_t metadata_peer_limit = const size_t metadata_peer_limit =
t->settings[GRPC_PEER_SETTINGS] t->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
@ -1205,7 +1210,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit),
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
} else { } else {
if (contains_non_ok_status(op->send_initial_metadata)) { if (contains_non_ok_status(s->send_initial_metadata)) {
s->seen_error = true; s->seen_error = true;
} }
if (!s->write_closed) { if (!s->write_closed) {
@ -1225,8 +1230,9 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
GPR_ASSERT(s->id != 0); GPR_ASSERT(s->id != 0);
grpc_chttp2_stream_write_type write_type = grpc_chttp2_stream_write_type write_type =
GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED; GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED;
if (op->send_message != NULL && if (op->send_message &&
(op->send_message->flags & GRPC_WRITE_BUFFER_HINT)) { (op->payload->send_message.send_message->flags &
GRPC_WRITE_BUFFER_HINT)) {
write_type = GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK; write_type = GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK;
} }
grpc_chttp2_become_writable(exec_ctx, t, s, write_type, grpc_chttp2_become_writable(exec_ctx, t, s, write_type,
@ -1244,7 +1250,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
} }
} }
if (op->send_message != NULL) { if (op->send_message) {
on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
s->fetching_send_message_finished = add_closure_barrier(op->on_complete); s->fetching_send_message_finished = add_closure_barrier(op->on_complete);
if (s->write_closed) { if (s->write_closed) {
@ -1258,14 +1264,14 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
GPR_ASSERT(s->fetching_send_message == NULL); GPR_ASSERT(s->fetching_send_message == NULL);
uint8_t *frame_hdr = uint8_t *frame_hdr =
grpc_slice_buffer_tiny_add(&s->flow_controlled_buffer, 5); grpc_slice_buffer_tiny_add(&s->flow_controlled_buffer, 5);
uint32_t flags = op->send_message->flags; uint32_t flags = op_payload->send_message.send_message->flags;
frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0;
size_t len = op->send_message->length; size_t len = op_payload->send_message.send_message->length;
frame_hdr[1] = (uint8_t)(len >> 24); frame_hdr[1] = (uint8_t)(len >> 24);
frame_hdr[2] = (uint8_t)(len >> 16); frame_hdr[2] = (uint8_t)(len >> 16);
frame_hdr[3] = (uint8_t)(len >> 8); frame_hdr[3] = (uint8_t)(len >> 8);
frame_hdr[4] = (uint8_t)(len); frame_hdr[4] = (uint8_t)(len);
s->fetching_send_message = op->send_message; s->fetching_send_message = op_payload->send_message.send_message;
s->fetched_send_message_length = 0; s->fetched_send_message_length = 0;
s->next_message_end_offset = s->flow_controlled_bytes_written + s->next_message_end_offset = s->flow_controlled_bytes_written +
(int64_t)s->flow_controlled_buffer.length + (int64_t)s->flow_controlled_buffer.length +
@ -1282,14 +1288,15 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
} }
} }
if (op->send_trailing_metadata != NULL) { if (op->send_trailing_metadata) {
GPR_ASSERT(s->send_trailing_metadata_finished == NULL); GPR_ASSERT(s->send_trailing_metadata_finished == NULL);
on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE;
s->send_trailing_metadata_finished = add_closure_barrier(on_complete); s->send_trailing_metadata_finished = add_closure_barrier(on_complete);
s->send_trailing_metadata = op->send_trailing_metadata; s->send_trailing_metadata =
op_payload->send_trailing_metadata.send_trailing_metadata;
s->write_buffering = false; s->write_buffering = false;
const size_t metadata_size = const size_t metadata_size =
grpc_metadata_batch_size(op->send_trailing_metadata); grpc_metadata_batch_size(s->send_trailing_metadata);
const size_t metadata_peer_limit = const size_t metadata_peer_limit =
t->settings[GRPC_PEER_SETTINGS] t->settings[GRPC_PEER_SETTINGS]
[GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE];
@ -1306,14 +1313,15 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit),
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED));
} else { } else {
if (contains_non_ok_status(op->send_trailing_metadata)) { if (contains_non_ok_status(s->send_trailing_metadata)) {
s->seen_error = true; s->seen_error = true;
} }
if (s->write_closed) { if (s->write_closed) {
s->send_trailing_metadata = NULL; s->send_trailing_metadata = NULL;
grpc_chttp2_complete_closure_step( grpc_chttp2_complete_closure_step(
exec_ctx, t, s, &s->send_trailing_metadata_finished, exec_ctx, t, s, &s->send_trailing_metadata_finished,
grpc_metadata_batch_is_empty(op->send_trailing_metadata) grpc_metadata_batch_is_empty(
op->payload->send_trailing_metadata.send_trailing_metadata)
? GRPC_ERROR_NONE ? GRPC_ERROR_NONE
: GRPC_ERROR_CREATE_FROM_STATIC_STRING( : GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Attempt to send trailing metadata after " "Attempt to send trailing metadata after "
@ -1329,17 +1337,19 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
} }
} }
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
GPR_ASSERT(s->recv_initial_metadata_ready == NULL); GPR_ASSERT(s->recv_initial_metadata_ready == NULL);
s->recv_initial_metadata_ready = op->recv_initial_metadata_ready; s->recv_initial_metadata_ready =
s->recv_initial_metadata = op->recv_initial_metadata; op_payload->recv_initial_metadata.recv_initial_metadata_ready;
s->recv_initial_metadata =
op_payload->recv_initial_metadata.recv_initial_metadata;
grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s);
} }
if (op->recv_message != NULL) { if (op->recv_message) {
GPR_ASSERT(s->recv_message_ready == NULL); GPR_ASSERT(s->recv_message_ready == NULL);
s->recv_message_ready = op->recv_message_ready; s->recv_message_ready = op_payload->recv_message.recv_message_ready;
s->recv_message = op->recv_message; s->recv_message = op_payload->recv_message.recv_message;
if (s->id != 0 && if (s->id != 0 &&
(s->incoming_frames.head == NULL || s->incoming_frames.head->is_tail)) { (s->incoming_frames.head == NULL || s->incoming_frames.head->is_tail)) {
incoming_byte_stream_update_flow_control(exec_ctx, t, s, 5, 0); incoming_byte_stream_update_flow_control(exec_ctx, t, s, 5, 0);
@ -1347,10 +1357,11 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s);
} }
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
GPR_ASSERT(s->recv_trailing_metadata_finished == NULL); GPR_ASSERT(s->recv_trailing_metadata_finished == NULL);
s->recv_trailing_metadata_finished = add_closure_barrier(on_complete); s->recv_trailing_metadata_finished = add_closure_barrier(on_complete);
s->recv_trailing_metadata = op->recv_trailing_metadata; s->recv_trailing_metadata =
op_payload->recv_trailing_metadata.recv_trailing_metadata;
s->final_metadata_requested = true; s->final_metadata_requested = true;
grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
} }
@ -1363,19 +1374,19 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
} }
static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
grpc_stream *gs, grpc_transport_stream_op *op) { grpc_stream *gs,
grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("perform_stream_op", 0); GPR_TIMER_BEGIN("perform_stream_op", 0);
grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs;
if (grpc_http_trace) { if (grpc_http_trace) {
char *str = grpc_transport_stream_op_string(op); char *str = grpc_transport_stream_op_batch_string(op);
gpr_log(GPR_DEBUG, "perform_stream_op[s=%p/%d]: %s", s, s->id, str); gpr_log(GPR_DEBUG, "perform_stream_op[s=%p/%d]: %s", s, s->id, str);
gpr_free(str); gpr_free(str);
} }
op->handler_private.args[0] = gt; op->handler_private.extra_arg = gs;
op->handler_private.args[1] = gs;
GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op"); GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op");
grpc_closure_sched( grpc_closure_sched(
exec_ctx, exec_ctx,
@ -1452,7 +1463,7 @@ static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
void *stream_op, void *stream_op,
grpc_error *error_ignored) { grpc_error *error_ignored) {
grpc_transport_op *op = stream_op; grpc_transport_op *op = stream_op;
grpc_chttp2_transport *t = op->transport_private.args[0]; grpc_chttp2_transport *t = op->handler_private.extra_arg;
grpc_error *close_transport = op->disconnect_with_error; grpc_error *close_transport = op->disconnect_with_error;
if (op->on_connectivity_state_change != NULL) { if (op->on_connectivity_state_change != NULL) {
@ -1498,10 +1509,10 @@ static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
char *msg = grpc_transport_op_string(op); char *msg = grpc_transport_op_string(op);
gpr_free(msg); gpr_free(msg);
op->transport_private.args[0] = gt; op->handler_private.extra_arg = gt;
GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op"); GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op");
grpc_closure_sched( grpc_closure_sched(
exec_ctx, grpc_closure_init(&op->transport_private.closure, exec_ctx, grpc_closure_init(&op->handler_private.closure,
perform_transport_op_locked, op, perform_transport_op_locked, op,
grpc_combiner_scheduler(t->combiner, false)), grpc_combiner_scheduler(t->combiner, false)),
GRPC_ERROR_NONE); GRPC_ERROR_NONE);

@ -172,7 +172,7 @@ struct op_state {
}; };
struct op_and_state { struct op_and_state {
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
struct op_state state; struct op_state state;
bool done; bool done;
struct stream_obj *s; /* Pointer back to the stream object */ struct stream_obj *s; /* Pointer back to the stream object */
@ -187,7 +187,7 @@ struct op_storage {
struct stream_obj { struct stream_obj {
gpr_arena *arena; gpr_arena *arena;
struct op_and_state *oas; struct op_and_state *oas;
grpc_transport_stream_op *curr_op; grpc_transport_stream_op_batch *curr_op;
grpc_cronet_transport *curr_ct; grpc_cronet_transport *curr_ct;
grpc_stream *curr_gs; grpc_stream *curr_gs;
bidirectional_stream *cbs; bidirectional_stream *cbs;
@ -298,12 +298,13 @@ static grpc_error *make_error_with_desc(int error_code, const char *desc) {
/* /*
Add a new stream op to op storage. Add a new stream op to op storage.
*/ */
static void add_to_storage(struct stream_obj *s, grpc_transport_stream_op *op) { static void add_to_storage(struct stream_obj *s,
grpc_transport_stream_op_batch *op) {
struct op_storage *storage = &s->storage; struct op_storage *storage = &s->storage;
/* add new op at the beginning of the linked list. The memory is freed /* add new op at the beginning of the linked list. The memory is freed
in remove_from_storage */ in remove_from_storage */
struct op_and_state *new_op = gpr_malloc(sizeof(struct op_and_state)); struct op_and_state *new_op = gpr_malloc(sizeof(struct op_and_state));
memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op)); memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op_batch));
memset(&new_op->state, 0, sizeof(new_op->state)); memset(&new_op->state, 0, sizeof(new_op->state));
new_op->s = s; new_op->s = s;
new_op->done = false; new_op->done = false;
@ -768,7 +769,7 @@ static bool header_has_authority(grpc_linked_mdelem *head) {
Op Execution: Decide if one of the actions contained in the stream op can be Op Execution: Decide if one of the actions contained in the stream op can be
executed. This is the heart of the state machine. executed. This is the heart of the state machine.
*/ */
static bool op_can_be_run(grpc_transport_stream_op *curr_op, static bool op_can_be_run(grpc_transport_stream_op_batch *curr_op,
struct stream_obj *s, struct op_state *op_state, struct stream_obj *s, struct op_state *op_state,
enum e_op_id op_id) { enum e_op_id op_id) {
struct op_state *stream_state = &s->state; struct op_state *stream_state = &s->state;
@ -919,7 +920,7 @@ static bool op_can_be_run(grpc_transport_stream_op *curr_op,
*/ */
static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
struct op_and_state *oas) { struct op_and_state *oas) {
grpc_transport_stream_op *stream_op = &oas->op; grpc_transport_stream_op_batch *stream_op = &oas->op;
struct stream_obj *s = oas->s; struct stream_obj *s = oas->s;
grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct;
struct op_state *stream_state = &s->state; struct op_state *stream_state = &s->state;
@ -941,9 +942,10 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
char *url = NULL; char *url = NULL;
const char *method = "POST"; const char *method = "POST";
s->header_array.headers = NULL; s->header_array.headers = NULL;
convert_metadata_to_cronet_headers( convert_metadata_to_cronet_headers(stream_op->payload->send_initial_metadata
stream_op->send_initial_metadata->list.head, t->host, &url, .send_initial_metadata->list.head,
&s->header_array.headers, &s->header_array.count, &method); t->host, &url, &s->header_array.headers,
&s->header_array.count, &method);
s->header_array.capacity = s->header_array.count; s->header_array.capacity = s->header_array.count;
CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url); CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url);
bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false); bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false);
@ -971,8 +973,9 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
grpc_slice_buffer write_slice_buffer; grpc_slice_buffer write_slice_buffer;
grpc_slice slice; grpc_slice slice;
grpc_slice_buffer_init(&write_slice_buffer); grpc_slice_buffer_init(&write_slice_buffer);
grpc_byte_stream_next(NULL, stream_op->send_message, &slice, grpc_byte_stream_next(
stream_op->send_message->length, NULL); NULL, stream_op->payload->send_message.send_message, &slice,
stream_op->payload->send_message.send_message->length, NULL);
grpc_slice_buffer_add(&write_slice_buffer, slice); grpc_slice_buffer_add(&write_slice_buffer, slice);
if (write_slice_buffer.count != 1) { if (write_slice_buffer.count != 1) {
/* Empty request not handled yet */ /* Empty request not handled yet */
@ -982,7 +985,8 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
if (write_slice_buffer.count > 0) { if (write_slice_buffer.count > 0) {
size_t write_buffer_size; size_t write_buffer_size;
create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer, create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer,
&write_buffer_size, stream_op->send_message->flags); &write_buffer_size,
stream_op->payload->send_message.send_message->flags);
CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs,
stream_state->ws.write_buffer); stream_state->ws.write_buffer);
stream_state->state_callback_received[OP_SEND_MESSAGE] = false; stream_state->state_callback_received[OP_SEND_MESSAGE] = false;
@ -1029,20 +1033,28 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
OP_RECV_INITIAL_METADATA)) { OP_RECV_INITIAL_METADATA)) {
CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_INITIAL_METADATA", oas); CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_INITIAL_METADATA", oas);
if (stream_state->state_op_done[OP_CANCEL_ERROR]) { if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx,
stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_NONE);
} else if (stream_state->state_callback_received[OP_FAILED]) { } else if (stream_state->state_callback_received[OP_FAILED]) {
grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx,
stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_NONE);
} else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { } else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) {
grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx,
stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_NONE);
} else { } else {
grpc_chttp2_incoming_metadata_buffer_publish( grpc_chttp2_incoming_metadata_buffer_publish(
exec_ctx, &oas->s->state.rs.initial_metadata, exec_ctx, &oas->s->state.rs.initial_metadata,
stream_op->recv_initial_metadata); stream_op->payload->recv_initial_metadata.recv_initial_metadata);
grpc_closure_sched(exec_ctx, stream_op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx,
stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_NONE);
} }
stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true; stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true;
result = ACTION_TAKEN_NO_CALLBACK; result = ACTION_TAKEN_NO_CALLBACK;
@ -1051,27 +1063,31 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_MESSAGE", oas); CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_MESSAGE", oas);
if (stream_state->state_op_done[OP_CANCEL_ERROR]) { if (stream_state->state_op_done[OP_CANCEL_ERROR]) {
CRONET_LOG(GPR_DEBUG, "Stream is cancelled."); CRONET_LOG(GPR_DEBUG, "Stream is cancelled.");
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(exec_ctx,
stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
result = ACTION_TAKEN_NO_CALLBACK; result = ACTION_TAKEN_NO_CALLBACK;
} else if (stream_state->state_callback_received[OP_FAILED]) { } else if (stream_state->state_callback_received[OP_FAILED]) {
CRONET_LOG(GPR_DEBUG, "Stream failed."); CRONET_LOG(GPR_DEBUG, "Stream failed.");
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(exec_ctx,
stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
result = ACTION_TAKEN_NO_CALLBACK; result = ACTION_TAKEN_NO_CALLBACK;
} else if (stream_state->rs.read_stream_closed == true) { } else if (stream_state->rs.read_stream_closed == true) {
/* No more data will be received */ /* No more data will be received */
CRONET_LOG(GPR_DEBUG, "read stream closed"); CRONET_LOG(GPR_DEBUG, "read stream closed");
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(exec_ctx,
stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
oas->state.state_op_done[OP_RECV_MESSAGE] = true; oas->state.state_op_done[OP_RECV_MESSAGE] = true;
result = ACTION_TAKEN_NO_CALLBACK; result = ACTION_TAKEN_NO_CALLBACK;
} else if (stream_state->flush_read) { } else if (stream_state->flush_read) {
CRONET_LOG(GPR_DEBUG, "flush read"); CRONET_LOG(GPR_DEBUG, "flush read");
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(exec_ctx,
stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
oas->state.state_op_done[OP_RECV_MESSAGE] = true; oas->state.state_op_done[OP_RECV_MESSAGE] = true;
@ -1110,8 +1126,9 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
} }
*((grpc_byte_buffer **)stream_op->recv_message) = *((grpc_byte_buffer **)stream_op->recv_message) =
(grpc_byte_buffer *)&stream_state->rs.sbs; (grpc_byte_buffer *)&stream_state->rs.sbs;
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx, stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
oas->state.state_op_done[OP_RECV_MESSAGE] = true; oas->state.state_op_done[OP_RECV_MESSAGE] = true;
@ -1163,7 +1180,8 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
} }
*((grpc_byte_buffer **)stream_op->recv_message) = *((grpc_byte_buffer **)stream_op->recv_message) =
(grpc_byte_buffer *)&stream_state->rs.sbs; (grpc_byte_buffer *)&stream_state->rs.sbs;
grpc_closure_sched(exec_ctx, stream_op->recv_message_ready, grpc_closure_sched(exec_ctx,
stream_op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE); GRPC_ERROR_NONE);
stream_state->state_op_done[OP_RECV_MESSAGE] = true; stream_state->state_op_done[OP_RECV_MESSAGE] = true;
oas->state.state_op_done[OP_RECV_MESSAGE] = true; oas->state.state_op_done[OP_RECV_MESSAGE] = true;
@ -1187,12 +1205,12 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
if (oas->s->state.rs.trailing_metadata_valid) { if (oas->s->state.rs.trailing_metadata_valid) {
grpc_chttp2_incoming_metadata_buffer_publish( grpc_chttp2_incoming_metadata_buffer_publish(
exec_ctx, &oas->s->state.rs.trailing_metadata, exec_ctx, &oas->s->state.rs.trailing_metadata,
stream_op->recv_trailing_metadata); stream_op->payload->recv_trailing_metadata.recv_trailing_metadata);
stream_state->rs.trailing_metadata_valid = false; stream_state->rs.trailing_metadata_valid = false;
} }
stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true; stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true;
result = ACTION_TAKEN_NO_CALLBACK; result = ACTION_TAKEN_NO_CALLBACK;
} else if (stream_op->cancel_error && } else if (stream_op->cancel_stream &&
op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) { op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) {
CRONET_LOG(GPR_DEBUG, "running: %p OP_CANCEL_ERROR", oas); CRONET_LOG(GPR_DEBUG, "running: %p OP_CANCEL_ERROR", oas);
CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs); CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs);
@ -1204,7 +1222,8 @@ static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
} }
stream_state->state_op_done[OP_CANCEL_ERROR] = true; stream_state->state_op_done[OP_CANCEL_ERROR] = true;
if (!stream_state->cancel_error) { if (!stream_state->cancel_error) {
stream_state->cancel_error = GRPC_ERROR_REF(stream_op->cancel_error); stream_state->cancel_error =
GRPC_ERROR_REF(stream_op->payload->cancel_stream.cancel_error);
} }
} else if (stream_op->on_complete && } else if (stream_op->on_complete &&
op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) { op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) {
@ -1283,18 +1302,22 @@ static void set_pollset_set_do_nothing(grpc_exec_ctx *exec_ctx,
grpc_pollset_set *pollset_set) {} grpc_pollset_set *pollset_set) {}
static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
grpc_stream *gs, grpc_transport_stream_op *op) { grpc_stream *gs,
grpc_transport_stream_op_batch *op) {
CRONET_LOG(GPR_DEBUG, "perform_stream_op"); CRONET_LOG(GPR_DEBUG, "perform_stream_op");
if (op->send_initial_metadata && if (op->send_initial_metadata &&
header_has_authority(op->send_initial_metadata->list.head)) { header_has_authority(op->payload->send_initial_metadata
.send_initial_metadata->list.head)) {
/* Cronet does not support :authority header field. We cancel the call when /* Cronet does not support :authority header field. We cancel the call when
this field is present in metadata */ this field is present in metadata */
if (op->recv_initial_metadata_ready) { if (op->recv_initial_metadata) {
grpc_closure_sched(exec_ctx, op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_CANCELLED); exec_ctx,
op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_CANCELLED);
} }
if (op->recv_message_ready) { if (op->recv_message) {
grpc_closure_sched(exec_ctx, op->recv_message_ready, grpc_closure_sched(exec_ctx, op->payload->recv_message.recv_message_ready,
GRPC_ERROR_CANCELLED); GRPC_ERROR_CANCELLED);
} }
grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_CANCELLED); grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_CANCELLED);

@ -246,9 +246,9 @@ void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack,
} }
void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
grpc_call_element *next_elem = elem + 1; grpc_call_element *next_elem = elem + 1;
next_elem->filter->start_transport_stream_op(exec_ctx, next_elem, op); next_elem->filter->start_transport_stream_op_batch(exec_ctx, next_elem, op);
} }
char *grpc_call_next_get_peer(grpc_exec_ctx *exec_ctx, char *grpc_call_next_get_peer(grpc_exec_ctx *exec_ctx,
@ -284,7 +284,8 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) {
void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx, void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_error *error) { grpc_error *error) {
grpc_transport_stream_op *op = grpc_make_transport_stream_op(NULL); grpc_transport_stream_op_batch *op = grpc_make_transport_stream_op(NULL);
op->cancel_error = error; op->cancel_stream = true;
elem->filter->start_transport_stream_op(exec_ctx, elem, op); op->payload->cancel_stream.cancel_error = error;
elem->filter->start_transport_stream_op_batch(exec_ctx, elem, op);
} }

@ -112,9 +112,9 @@ typedef struct {
typedef struct { typedef struct {
/* Called to eg. send/receive data on a call. /* Called to eg. send/receive data on a call.
See grpc_call_next_op on how to call the next element in the stack */ See grpc_call_next_op on how to call the next element in the stack */
void (*start_transport_stream_op)(grpc_exec_ctx *exec_ctx, void (*start_transport_stream_op_batch)(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op); grpc_transport_stream_op_batch *op);
/* Called to handle channel level operations - e.g. new calls, or transport /* Called to handle channel level operations - e.g. new calls, or transport
closure. closure.
See grpc_channel_next_op on how to call the next element in the stack */ See grpc_channel_next_op on how to call the next element in the stack */
@ -281,7 +281,7 @@ void grpc_call_stack_ignore_set_pollset_or_pollset_set(
grpc_polling_entity *pollent); grpc_polling_entity *pollent);
/* Call the next operation in a call stack */ /* Call the next operation in a call stack */
void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op); grpc_transport_stream_op_batch *op);
/* Call the next operation (depending on call directionality) in a channel /* Call the next operation (depending on call directionality) in a channel
stack */ stack */
void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
@ -300,7 +300,8 @@ grpc_channel_stack *grpc_channel_stack_from_top_element(
grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem); grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem);
void grpc_call_log_op(char *file, int line, gpr_log_severity severity, void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
grpc_call_element *elem, grpc_transport_stream_op *op); grpc_call_element *elem,
grpc_transport_stream_op_batch *op);
void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx, void grpc_call_element_signal_error(grpc_exec_ctx *exec_ctx,
grpc_call_element *cur_elem, grpc_call_element *cur_elem,

@ -62,9 +62,9 @@ typedef struct connected_channel_call_data { void *unused; } call_data;
/* Intercept a call operation and either push it directly up or translate it /* Intercept a call operation and either push it directly up or translate it
into transport stream operations */ into transport stream operations */
static void con_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void con_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
@ -142,7 +142,7 @@ static void con_get_channel_info(grpc_exec_ctx *exec_ctx,
const grpc_channel_info *channel_info) {} const grpc_channel_info *channel_info) {}
const grpc_channel_filter grpc_connected_filter = { const grpc_channel_filter grpc_connected_filter = {
con_start_transport_stream_op, con_start_transport_stream_op_batch,
con_start_transport_op, con_start_transport_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -56,6 +56,7 @@
#include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/ev_posix.h"
#include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/iomgr/wakeup_fd_posix.h" #include "src/core/lib/iomgr/wakeup_fd_posix.h"
#include "src/core/lib/iomgr/workqueue.h" #include "src/core/lib/iomgr/workqueue.h"
#include "src/core/lib/profiling/timers.h" #include "src/core/lib/profiling/timers.h"
@ -1467,8 +1468,9 @@ static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
return 0; return 0;
} }
timeout = gpr_time_sub(deadline, now); timeout = gpr_time_sub(deadline, now);
return gpr_time_to_millis(gpr_time_add( int millis = gpr_time_to_millis(gpr_time_add(
timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
return millis >= 1 ? millis : 1;
} }
static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
@ -1650,6 +1652,7 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx,
for (int i = 0; i < ep_rv; ++i) { for (int i = 0; i < ep_rv; ++i) {
void *data_ptr = ep_ev[i].data.ptr; void *data_ptr = ep_ev[i].data.ptr;
if (data_ptr == &global_wakeup_fd) { if (data_ptr == &global_wakeup_fd) {
grpc_timer_consume_kick();
append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd),
err_desc); err_desc);
} else if (data_ptr == &pi->workqueue_wakeup_fd) { } else if (data_ptr == &pi->workqueue_wakeup_fd) {

@ -52,6 +52,7 @@
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
#include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/iomgr_internal.h"
#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/iomgr/wakeup_fd_cv.h" #include "src/core/lib/iomgr/wakeup_fd_cv.h"
#include "src/core/lib/iomgr/wakeup_fd_posix.h" #include "src/core/lib/iomgr/wakeup_fd_posix.h"
#include "src/core/lib/profiling/timers.h" #include "src/core/lib/profiling/timers.h"
@ -1006,6 +1007,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
} }
} else { } else {
if (pfds[0].revents & POLLIN_CHECK) { if (pfds[0].revents & POLLIN_CHECK) {
grpc_timer_consume_kick();
work_combine_error(&error, work_combine_error(&error,
grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd)); grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd));
} }

@ -101,6 +101,9 @@ bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now,
void grpc_timer_list_init(gpr_timespec now); void grpc_timer_list_init(gpr_timespec now);
void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx); void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx);
/* Consume a kick issued by grpc_kick_poller */
void grpc_timer_consume_kick(void);
/* the following must be implemented by each iomgr implementation */ /* the following must be implemented by each iomgr implementation */
void grpc_kick_poller(void); void grpc_kick_poller(void);

@ -37,9 +37,13 @@
#include "src/core/lib/iomgr/timer.h" #include "src/core/lib/iomgr/timer.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/tls.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/iomgr/time_averaged_stats.h" #include "src/core/lib/iomgr/time_averaged_stats.h"
#include "src/core/lib/iomgr/timer_heap.h" #include "src/core/lib/iomgr/timer_heap.h"
#include "src/core/lib/support/spinlock.h" #include "src/core/lib/support/spinlock.h"
@ -52,12 +56,15 @@
#define MIN_QUEUE_WINDOW_DURATION 0.01 #define MIN_QUEUE_WINDOW_DURATION 0.01
#define MAX_QUEUE_WINDOW_DURATION 1 #define MAX_QUEUE_WINDOW_DURATION 1
int grpc_timer_trace = 0;
int grpc_timer_check_trace = 0;
typedef struct { typedef struct {
gpr_mu mu; gpr_mu mu;
grpc_time_averaged_stats stats; grpc_time_averaged_stats stats;
/* All and only timers with deadlines <= this will be in the heap. */ /* All and only timers with deadlines <= this will be in the heap. */
gpr_timespec queue_deadline_cap; gpr_atm queue_deadline_cap;
gpr_timespec min_deadline; gpr_atm min_deadline;
/* Index in the g_shard_queue */ /* Index in the g_shard_queue */
uint32_t shard_queue_index; uint32_t shard_queue_index;
/* This holds all timers with deadlines < queue_deadline_cap. Timers in this /* This holds all timers with deadlines < queue_deadline_cap. Timers in this
@ -67,38 +74,92 @@ typedef struct {
grpc_timer list; grpc_timer list;
} shard_type; } shard_type;
/* Protects g_shard_queue */ struct shared_mutables {
static gpr_mu g_mu; gpr_atm min_timer;
/* Allow only one run_some_expired_timers at once */ /* Allow only one run_some_expired_timers at once */
static gpr_spinlock g_checker_mu = GPR_SPINLOCK_STATIC_INITIALIZER; gpr_spinlock checker_mu;
bool initialized;
/* Protects g_shard_queue */
gpr_mu mu;
} GPR_ALIGN_STRUCT(GPR_CACHELINE_SIZE);
static struct shared_mutables g_shared_mutables = {
.checker_mu = GPR_SPINLOCK_STATIC_INITIALIZER, .initialized = false,
};
static gpr_clock_type g_clock_type; static gpr_clock_type g_clock_type;
static shard_type g_shards[NUM_SHARDS]; static shard_type g_shards[NUM_SHARDS];
/* Protected by g_mu */ /* Protected by g_shared_mutables.mu */
static shard_type *g_shard_queue[NUM_SHARDS]; static shard_type *g_shard_queue[NUM_SHARDS];
static bool g_initialized = false; static gpr_timespec g_start_time;
GPR_TLS_DECL(g_last_seen_min_timer);
static gpr_atm saturating_add(gpr_atm a, gpr_atm b) {
if (a > GPR_ATM_MAX - b) {
return GPR_ATM_MAX;
}
return a + b;
}
static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_atm now,
gpr_atm *next, grpc_error *error);
static gpr_timespec dbl_to_ts(double d) {
gpr_timespec ts;
ts.tv_sec = (int64_t)d;
ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec));
ts.clock_type = GPR_TIMESPAN;
return ts;
}
static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) {
ts = gpr_time_sub(ts, g_start_time);
double x = GPR_MS_PER_SEC * (double)ts.tv_sec +
(double)ts.tv_nsec / GPR_NS_PER_MS +
(double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC;
if (x < 0) return 0;
if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
return (gpr_atm)x;
}
static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) {
ts = gpr_time_sub(ts, g_start_time);
double x =
GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS;
if (x < 0) return 0;
if (x > GPR_ATM_MAX) return GPR_ATM_MAX;
return (gpr_atm)x;
}
static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, static gpr_timespec atm_to_timespec(gpr_atm x) {
gpr_timespec *next, grpc_error *error); return gpr_time_add(g_start_time, dbl_to_ts((double)x / 1000.0));
}
static gpr_timespec compute_min_deadline(shard_type *shard) { static gpr_atm compute_min_deadline(shard_type *shard) {
return grpc_timer_heap_is_empty(&shard->heap) return grpc_timer_heap_is_empty(&shard->heap)
? shard->queue_deadline_cap ? saturating_add(shard->queue_deadline_cap, 1)
: grpc_timer_heap_top(&shard->heap)->deadline; : grpc_timer_heap_top(&shard->heap)->deadline;
} }
void grpc_timer_list_init(gpr_timespec now) { void grpc_timer_list_init(gpr_timespec now) {
uint32_t i; uint32_t i;
g_initialized = true; g_shared_mutables.initialized = true;
gpr_mu_init(&g_mu); gpr_mu_init(&g_shared_mutables.mu);
g_clock_type = now.clock_type; g_clock_type = now.clock_type;
g_start_time = now;
g_shared_mutables.min_timer = timespec_to_atm_round_down(now);
gpr_tls_init(&g_last_seen_min_timer);
gpr_tls_set(&g_last_seen_min_timer, 0);
grpc_register_tracer("timer", &grpc_timer_trace);
grpc_register_tracer("timer_check", &grpc_timer_check_trace);
for (i = 0; i < NUM_SHARDS; i++) { for (i = 0; i < NUM_SHARDS; i++) {
shard_type *shard = &g_shards[i]; shard_type *shard = &g_shards[i];
gpr_mu_init(&shard->mu); gpr_mu_init(&shard->mu);
grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1, grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1,
0.5); 0.5);
shard->queue_deadline_cap = now; shard->queue_deadline_cap = g_shared_mutables.min_timer;
shard->shard_queue_index = i; shard->shard_queue_index = i;
grpc_timer_heap_init(&shard->heap); grpc_timer_heap_init(&shard->heap);
shard->list.next = shard->list.prev = &shard->list; shard->list.next = shard->list.prev = &shard->list;
@ -110,29 +171,23 @@ void grpc_timer_list_init(gpr_timespec now) {
void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) { void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {
int i; int i;
run_some_expired_timers( run_some_expired_timers(
exec_ctx, gpr_inf_future(g_clock_type), NULL, exec_ctx, GPR_ATM_MAX, NULL,
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown")); GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown"));
for (i = 0; i < NUM_SHARDS; i++) { for (i = 0; i < NUM_SHARDS; i++) {
shard_type *shard = &g_shards[i]; shard_type *shard = &g_shards[i];
gpr_mu_destroy(&shard->mu); gpr_mu_destroy(&shard->mu);
grpc_timer_heap_destroy(&shard->heap); grpc_timer_heap_destroy(&shard->heap);
} }
gpr_mu_destroy(&g_mu); gpr_mu_destroy(&g_shared_mutables.mu);
g_initialized = false; gpr_tls_destroy(&g_last_seen_min_timer);
g_shared_mutables.initialized = false;
} }
static double ts_to_dbl(gpr_timespec ts) { static double ts_to_dbl(gpr_timespec ts) {
return (double)ts.tv_sec + 1e-9 * ts.tv_nsec; return (double)ts.tv_sec + 1e-9 * ts.tv_nsec;
} }
static gpr_timespec dbl_to_ts(double d) { /* returns true if the first element in the list */
gpr_timespec ts;
ts.tv_sec = (int64_t)d;
ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec));
ts.clock_type = GPR_TIMESPAN;
return ts;
}
static void list_join(grpc_timer *head, grpc_timer *timer) { static void list_join(grpc_timer *head, grpc_timer *timer) {
timer->next = head; timer->next = head;
timer->prev = head->prev; timer->prev = head->prev;
@ -158,15 +213,13 @@ static void swap_adjacent_shards_in_queue(uint32_t first_shard_queue_index) {
static void note_deadline_change(shard_type *shard) { static void note_deadline_change(shard_type *shard) {
while (shard->shard_queue_index > 0 && while (shard->shard_queue_index > 0 &&
gpr_time_cmp( shard->min_deadline <
shard->min_deadline, g_shard_queue[shard->shard_queue_index - 1]->min_deadline) {
g_shard_queue[shard->shard_queue_index - 1]->min_deadline) < 0) {
swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); swap_adjacent_shards_in_queue(shard->shard_queue_index - 1);
} }
while (shard->shard_queue_index < NUM_SHARDS - 1 && while (shard->shard_queue_index < NUM_SHARDS - 1 &&
gpr_time_cmp( shard->min_deadline >
shard->min_deadline, g_shard_queue[shard->shard_queue_index + 1]->min_deadline) {
g_shard_queue[shard->shard_queue_index + 1]->min_deadline) > 0) {
swap_adjacent_shards_in_queue(shard->shard_queue_index); swap_adjacent_shards_in_queue(shard->shard_queue_index);
} }
} }
@ -179,9 +232,17 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
GPR_ASSERT(deadline.clock_type == g_clock_type); GPR_ASSERT(deadline.clock_type == g_clock_type);
GPR_ASSERT(now.clock_type == g_clock_type); GPR_ASSERT(now.clock_type == g_clock_type);
timer->closure = closure; timer->closure = closure;
timer->deadline = deadline; timer->deadline = timespec_to_atm_round_up(deadline);
if (grpc_timer_trace) {
gpr_log(GPR_DEBUG, "TIMER %p: SET %" PRId64 ".%09d [%" PRIdPTR
"] now %" PRId64 ".%09d [%" PRIdPTR "] call %p[%p]",
timer, deadline.tv_sec, deadline.tv_nsec, timer->deadline,
now.tv_sec, now.tv_nsec, timespec_to_atm_round_down(now), closure,
closure->cb);
}
if (!g_initialized) { if (!g_shared_mutables.initialized) {
timer->pending = false; timer->pending = false;
grpc_closure_sched(exec_ctx, timer->closure, grpc_closure_sched(exec_ctx, timer->closure,
GRPC_ERROR_CREATE_FROM_STATIC_STRING( GRPC_ERROR_CREATE_FROM_STATIC_STRING(
@ -201,12 +262,18 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
grpc_time_averaged_stats_add_sample(&shard->stats, grpc_time_averaged_stats_add_sample(&shard->stats,
ts_to_dbl(gpr_time_sub(deadline, now))); ts_to_dbl(gpr_time_sub(deadline, now)));
if (gpr_time_cmp(deadline, shard->queue_deadline_cap) < 0) { if (timer->deadline < shard->queue_deadline_cap) {
is_first_timer = grpc_timer_heap_add(&shard->heap, timer); is_first_timer = grpc_timer_heap_add(&shard->heap, timer);
} else { } else {
timer->heap_index = INVALID_HEAP_INDEX; timer->heap_index = INVALID_HEAP_INDEX;
list_join(&shard->list, timer); list_join(&shard->list, timer);
} }
if (grpc_timer_trace) {
gpr_log(GPR_DEBUG, " .. add to shard %d with queue_deadline_cap=%" PRIdPTR
" => is_first_timer=%s",
(int)(shard - g_shards), shard->queue_deadline_cap,
is_first_timer ? "true" : "false");
}
gpr_mu_unlock(&shard->mu); gpr_mu_unlock(&shard->mu);
/* Deadline may have decreased, we need to adjust the master queue. Note /* Deadline may have decreased, we need to adjust the master queue. Note
@ -221,28 +288,41 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer,
In that case, the timer will simply have to wait for the next In that case, the timer will simply have to wait for the next
grpc_timer_check. */ grpc_timer_check. */
if (is_first_timer) { if (is_first_timer) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_shared_mutables.mu);
if (gpr_time_cmp(deadline, shard->min_deadline) < 0) { if (grpc_timer_trace) {
gpr_timespec old_min_deadline = g_shard_queue[0]->min_deadline; gpr_log(GPR_DEBUG, " .. old shard min_deadline=%" PRIdPTR,
shard->min_deadline = deadline; shard->min_deadline);
}
if (timer->deadline < shard->min_deadline) {
gpr_atm old_min_deadline = g_shard_queue[0]->min_deadline;
shard->min_deadline = timer->deadline;
note_deadline_change(shard); note_deadline_change(shard);
if (shard->shard_queue_index == 0 && if (shard->shard_queue_index == 0 && timer->deadline < old_min_deadline) {
gpr_time_cmp(deadline, old_min_deadline) < 0) { gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, timer->deadline);
grpc_kick_poller(); grpc_kick_poller();
} }
} }
gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_shared_mutables.mu);
} }
} }
void grpc_timer_consume_kick(void) {
/* force re-evaluation of last seeen min */
gpr_tls_set(&g_last_seen_min_timer, 0);
}
void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
if (!g_initialized) { if (!g_shared_mutables.initialized) {
/* must have already been cancelled, also the shard mutex is invalid */ /* must have already been cancelled, also the shard mutex is invalid */
return; return;
} }
shard_type *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)]; shard_type *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)];
gpr_mu_lock(&shard->mu); gpr_mu_lock(&shard->mu);
if (grpc_timer_trace) {
gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer,
timer->pending ? "true" : "false");
}
if (timer->pending) { if (timer->pending) {
grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED); grpc_closure_sched(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED);
timer->pending = false; timer->pending = false;
@ -260,7 +340,7 @@ void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
for timers that fall at or under it. Returns true if the queue is no for timers that fall at or under it. Returns true if the queue is no
longer empty. longer empty.
REQUIRES: shard->mu locked */ REQUIRES: shard->mu locked */
static int refill_queue(shard_type *shard, gpr_timespec now) { static int refill_queue(shard_type *shard, gpr_atm now) {
/* Compute the new queue window width and bound by the limits: */ /* Compute the new queue window width and bound by the limits: */
double computed_deadline_delta = double computed_deadline_delta =
grpc_time_averaged_stats_update_average(&shard->stats) * grpc_time_averaged_stats_update_average(&shard->stats) *
@ -271,12 +351,22 @@ static int refill_queue(shard_type *shard, gpr_timespec now) {
grpc_timer *timer, *next; grpc_timer *timer, *next;
/* Compute the new cap and put all timers under it into the queue: */ /* Compute the new cap and put all timers under it into the queue: */
shard->queue_deadline_cap = gpr_time_add( shard->queue_deadline_cap =
gpr_time_max(now, shard->queue_deadline_cap), dbl_to_ts(deadline_delta)); saturating_add(GPR_MAX(now, shard->queue_deadline_cap),
(gpr_atm)(deadline_delta * 1000.0));
if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG, " .. shard[%d]->queue_deadline_cap --> %" PRIdPTR,
(int)(shard - g_shards), shard->queue_deadline_cap);
}
for (timer = shard->list.next; timer != &shard->list; timer = next) { for (timer = shard->list.next; timer != &shard->list; timer = next) {
next = timer->next; next = timer->next;
if (gpr_time_cmp(timer->deadline, shard->queue_deadline_cap) < 0) { if (timer->deadline < shard->queue_deadline_cap) {
if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG, " .. add timer with deadline %" PRIdPTR " to heap",
timer->deadline);
}
list_remove(timer); list_remove(timer);
grpc_timer_heap_add(&shard->heap, timer); grpc_timer_heap_add(&shard->heap, timer);
} }
@ -287,15 +377,29 @@ static int refill_queue(shard_type *shard, gpr_timespec now) {
/* This pops the next non-cancelled timer with deadline <= now from the /* This pops the next non-cancelled timer with deadline <= now from the
queue, or returns NULL if there isn't one. queue, or returns NULL if there isn't one.
REQUIRES: shard->mu locked */ REQUIRES: shard->mu locked */
static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) { static grpc_timer *pop_one(shard_type *shard, gpr_atm now) {
grpc_timer *timer; grpc_timer *timer;
for (;;) { for (;;) {
if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG, " .. shard[%d]: heap_empty=%s",
(int)(shard - g_shards),
grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false");
}
if (grpc_timer_heap_is_empty(&shard->heap)) { if (grpc_timer_heap_is_empty(&shard->heap)) {
if (gpr_time_cmp(now, shard->queue_deadline_cap) < 0) return NULL; if (now < shard->queue_deadline_cap) return NULL;
if (!refill_queue(shard, now)) return NULL; if (!refill_queue(shard, now)) return NULL;
} }
timer = grpc_timer_heap_top(&shard->heap); timer = grpc_timer_heap_top(&shard->heap);
if (gpr_time_cmp(timer->deadline, now) > 0) return NULL; if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG,
" .. check top timer deadline=%" PRIdPTR " now=%" PRIdPTR,
timer->deadline, now);
}
if (timer->deadline > now) return NULL;
if (grpc_timer_trace) {
gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late", timer,
now - timer->deadline);
}
timer->pending = false; timer->pending = false;
grpc_timer_heap_pop(&shard->heap); grpc_timer_heap_pop(&shard->heap);
return timer; return timer;
@ -304,7 +408,7 @@ static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) {
/* REQUIRES: shard->mu unlocked */ /* REQUIRES: shard->mu unlocked */
static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard, static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
gpr_timespec now, gpr_timespec *new_min_deadline, gpr_atm now, gpr_atm *new_min_deadline,
grpc_error *error) { grpc_error *error) {
size_t n = 0; size_t n = 0;
grpc_timer *timer; grpc_timer *timer;
@ -318,17 +422,29 @@ static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
return n; return n;
} }
static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_atm now,
gpr_timespec *next, grpc_error *error) { gpr_atm *next, grpc_error *error) {
size_t n = 0; size_t n = 0;
/* TODO(ctiller): verify that there are any timers (atomically) here */ gpr_atm min_timer = gpr_atm_no_barrier_load(&g_shared_mutables.min_timer);
gpr_tls_set(&g_last_seen_min_timer, min_timer);
if (now < min_timer) {
if (next != NULL) *next = GPR_MIN(*next, min_timer);
return 0;
}
if (gpr_spinlock_trylock(&g_checker_mu)) { if (gpr_spinlock_trylock(&g_shared_mutables.checker_mu)) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_shared_mutables.mu);
while (gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { if (grpc_timer_check_trace) {
gpr_timespec new_min_deadline; gpr_log(GPR_DEBUG, " .. shard[%d]->min_deadline = %" PRIdPTR,
(int)(g_shard_queue[0] - g_shards),
g_shard_queue[0]->min_deadline);
}
while (g_shard_queue[0]->min_deadline < now ||
(now != GPR_ATM_MAX && g_shard_queue[0]->min_deadline == now)) {
gpr_atm new_min_deadline;
/* For efficiency, we pop as many available timers as we can from the /* For efficiency, we pop as many available timers as we can from the
shard. This may violate perfect timer deadline ordering, but that shard. This may violate perfect timer deadline ordering, but that
@ -336,6 +452,14 @@ static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
n += n +=
pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, error); pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, error);
if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG, " .. popped --> %" PRIdPTR
", shard[%d]->min_deadline %" PRIdPTR
" --> %" PRIdPTR ", now=%" PRIdPTR,
n, (int)(g_shard_queue[0] - g_shards),
g_shard_queue[0]->min_deadline, new_min_deadline, now);
}
/* An grpc_timer_init() on the shard could intervene here, adding a new /* An grpc_timer_init() on the shard could intervene here, adding a new
timer that is earlier than new_min_deadline. However, timer that is earlier than new_min_deadline. However,
grpc_timer_init() will block on the master_lock before it can call grpc_timer_init() will block on the master_lock before it can call
@ -346,23 +470,24 @@ static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
} }
if (next) { if (next) {
*next = gpr_time_min(*next, g_shard_queue[0]->min_deadline); *next = GPR_MIN(*next, g_shard_queue[0]->min_deadline);
} }
gpr_mu_unlock(&g_mu); gpr_atm_no_barrier_store(&g_shared_mutables.min_timer,
gpr_spinlock_unlock(&g_checker_mu); g_shard_queue[0]->min_deadline);
gpr_mu_unlock(&g_shared_mutables.mu);
gpr_spinlock_unlock(&g_shared_mutables.checker_mu);
} else if (next != NULL) { } else if (next != NULL) {
/* TODO(ctiller): this forces calling code to do an short poll, and /* TODO(ctiller): this forces calling code to do an short poll, and
then retry the timer check (because this time through the timer list was then retry the timer check (because this time through the timer list was
contended). contended).
We could reduce the cost here dramatically by keeping a count of how many We could reduce the cost here dramatically by keeping a count of how
currently active pollers got through the uncontended case above many currently active pollers got through the uncontended case above
successfully, and waking up other pollers IFF that count drops to zero. successfully, and waking up other pollers IFF that count drops to zero.
Once that count is in place, this entire else branch could disappear. */ Once that count is in place, this entire else branch could disappear. */
*next = gpr_time_min( *next = GPR_MIN(*next, now + 1);
*next, gpr_time_add(now, gpr_time_from_millis(1, GPR_TIMESPAN)));
} }
GRPC_ERROR_UNREF(error); GRPC_ERROR_UNREF(error);
@ -372,12 +497,71 @@ static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now,
gpr_timespec *next) { gpr_timespec *next) {
// prelude
GPR_ASSERT(now.clock_type == g_clock_type); GPR_ASSERT(now.clock_type == g_clock_type);
return run_some_expired_timers( gpr_atm now_atm = timespec_to_atm_round_down(now);
exec_ctx, now, next,
/* fetch from a thread-local first: this avoids contention on a globally
mutable cacheline in the common case */
gpr_atm min_timer = gpr_tls_get(&g_last_seen_min_timer);
if (now_atm < min_timer) {
if (next != NULL) {
*next =
atm_to_timespec(GPR_MIN(timespec_to_atm_round_up(*next), min_timer));
}
if (grpc_timer_check_trace) {
gpr_log(GPR_DEBUG,
"TIMER CHECK SKIP: now_atm=%" PRIdPTR " min_timer=%" PRIdPTR,
now_atm, min_timer);
}
return 0;
}
grpc_error *shutdown_error =
gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0 gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0
? GRPC_ERROR_NONE ? GRPC_ERROR_NONE
: GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system")); : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system");
// tracing
if (grpc_timer_check_trace) {
char *next_str;
if (next == NULL) {
next_str = gpr_strdup("NULL");
} else {
gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec,
next->tv_nsec, timespec_to_atm_round_down(*next));
}
gpr_log(GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRId64 ".%09d [%" PRIdPTR
"] next=%s tls_min=%" PRIdPTR " glob_min=%" PRIdPTR,
now.tv_sec, now.tv_nsec, now_atm, next_str,
gpr_tls_get(&g_last_seen_min_timer),
gpr_atm_no_barrier_load(&g_shared_mutables.min_timer));
gpr_free(next_str);
}
// actual code
bool r;
gpr_atm next_atm;
if (next == NULL) {
r = run_some_expired_timers(exec_ctx, now_atm, NULL, shutdown_error);
} else {
next_atm = timespec_to_atm_round_down(*next);
r = run_some_expired_timers(exec_ctx, now_atm, &next_atm, shutdown_error);
*next = atm_to_timespec(next_atm);
}
// tracing
if (grpc_timer_check_trace) {
char *next_str;
if (next == NULL) {
next_str = gpr_strdup("NULL");
} else {
gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec,
next->tv_nsec, next_atm);
}
gpr_log(GPR_DEBUG, "TIMER CHECK END: %d timers triggered; next=%s", r,
next_str);
gpr_free(next_str);
}
return r > 0;
} }
#endif /* GRPC_TIMER_USE_GENERIC */ #endif /* GRPC_TIMER_USE_GENERIC */

@ -38,7 +38,7 @@
#include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/exec_ctx.h"
struct grpc_timer { struct grpc_timer {
gpr_timespec deadline; gpr_atm deadline;
uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */
bool pending; bool pending;
struct grpc_timer *next; struct grpc_timer *next;

@ -50,7 +50,7 @@
static void adjust_upwards(grpc_timer **first, uint32_t i, grpc_timer *t) { static void adjust_upwards(grpc_timer **first, uint32_t i, grpc_timer *t) {
while (i > 0) { while (i > 0) {
uint32_t parent = (uint32_t)(((int)i - 1) / 2); uint32_t parent = (uint32_t)(((int)i - 1) / 2);
if (gpr_time_cmp(first[parent]->deadline, t->deadline) <= 0) break; if (first[parent]->deadline <= t->deadline) break;
first[i] = first[parent]; first[i] = first[parent];
first[i]->heap_index = i; first[i]->heap_index = i;
i = parent; i = parent;
@ -68,12 +68,12 @@ static void adjust_downwards(grpc_timer **first, uint32_t i, uint32_t length,
uint32_t left_child = 1u + 2u * i; uint32_t left_child = 1u + 2u * i;
if (left_child >= length) break; if (left_child >= length) break;
uint32_t right_child = left_child + 1; uint32_t right_child = left_child + 1;
uint32_t next_i = right_child < length && uint32_t next_i =
gpr_time_cmp(first[left_child]->deadline, right_child < length &&
first[right_child]->deadline) > 0 first[left_child]->deadline > first[right_child]->deadline
? right_child ? right_child
: left_child; : left_child;
if (gpr_time_cmp(t->deadline, first[next_i]->deadline) <= 0) break; if (t->deadline <= first[next_i]->deadline) break;
first[i] = first[next_i]; first[i] = first[next_i];
first[i]->heap_index = i; first[i]->heap_index = i;
i = next_i; i = next_i;
@ -97,7 +97,7 @@ static void maybe_shrink(grpc_timer_heap *heap) {
static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) { static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) {
uint32_t i = timer->heap_index; uint32_t i = timer->heap_index;
uint32_t parent = (uint32_t)(((int)i - 1) / 2); uint32_t parent = (uint32_t)(((int)i - 1) / 2);
if (gpr_time_cmp(heap->timers[parent]->deadline, timer->deadline) > 0) { if (heap->timers[parent]->deadline > timer->deadline) {
adjust_upwards(heap->timers, i, timer); adjust_upwards(heap->timers, i, timer);
} else { } else {
adjust_downwards(heap->timers, i, heap->timer_count, timer); adjust_downwards(heap->timers, i, heap->timer_count, timer);

@ -64,7 +64,7 @@ typedef struct {
pollset_set so that work can progress when this call wants work to progress pollset_set so that work can progress when this call wants work to progress
*/ */
grpc_polling_entity *pollent; grpc_polling_entity *pollent;
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
uint8_t security_context_set; uint8_t security_context_set;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
grpc_auth_metadata_context auth_md_context; grpc_auth_metadata_context auth_md_context;
@ -108,7 +108,7 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
const char *error_details) { const char *error_details) {
grpc_call_element *elem = (grpc_call_element *)user_data; grpc_call_element *elem = (grpc_call_element *)user_data;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
grpc_transport_stream_op *op = &calld->op; grpc_transport_stream_op_batch *op = &calld->op;
grpc_metadata_batch *mdb; grpc_metadata_batch *mdb;
size_t i; size_t i;
reset_auth_metadata_context(&calld->auth_md_context); reset_auth_metadata_context(&calld->auth_md_context);
@ -122,8 +122,8 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED); GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED);
} else { } else {
GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT); GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
GPR_ASSERT(op->send_initial_metadata != NULL); GPR_ASSERT(op->send_initial_metadata);
mdb = op->send_initial_metadata; mdb = op->payload->send_initial_metadata.send_initial_metadata;
for (i = 0; i < num_md; i++) { for (i = 0; i < num_md; i++) {
add_error(&error, add_error(&error,
grpc_metadata_batch_add_tail( grpc_metadata_batch_add_tail(
@ -136,7 +136,7 @@ static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
if (error == GRPC_ERROR_NONE) { if (error == GRPC_ERROR_NONE) {
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
} else { } else {
grpc_transport_stream_op_finish_with_failure(exec_ctx, op, error); grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error);
} }
} }
@ -172,11 +172,13 @@ void build_auth_metadata_context(grpc_security_connector *sc,
static void send_security_metadata(grpc_exec_ctx *exec_ctx, static void send_security_metadata(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
grpc_client_security_context *ctx = grpc_client_security_context *ctx =
(grpc_client_security_context *)op->context[GRPC_CONTEXT_SECURITY].value; (grpc_client_security_context *)op->payload
->context[GRPC_CONTEXT_SECURITY]
.value;
grpc_call_credentials *channel_call_creds = grpc_call_credentials *channel_call_creds =
chand->security_connector->request_metadata_creds; chand->security_connector->request_metadata_creds;
int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL); int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL);
@ -191,7 +193,7 @@ static void send_security_metadata(grpc_exec_ctx *exec_ctx,
calld->creds = grpc_composite_call_credentials_create(channel_call_creds, calld->creds = grpc_composite_call_credentials_create(channel_call_creds,
ctx->creds, NULL); ctx->creds, NULL);
if (calld->creds == NULL) { if (calld->creds == NULL) {
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, exec_ctx, op,
grpc_error_set_int( grpc_error_set_int(
GRPC_ERROR_CREATE_FROM_STATIC_STRING( GRPC_ERROR_CREATE_FROM_STATIC_STRING(
@ -242,7 +244,7 @@ static void on_host_checked(grpc_exec_ctx *exec_ctx, void *user_data,
that is being sent or received. */ that is being sent or received. */
static void auth_start_transport_op(grpc_exec_ctx *exec_ctx, static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GPR_TIMER_BEGIN("auth_start_transport_op", 0); GPR_TIMER_BEGIN("auth_start_transport_op", 0);
/* grab pointers to our data from the call element */ /* grab pointers to our data from the call element */
@ -251,23 +253,25 @@ static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_linked_mdelem *l; grpc_linked_mdelem *l;
grpc_client_security_context *sec_ctx = NULL; grpc_client_security_context *sec_ctx = NULL;
if (calld->security_context_set == 0 && op->cancel_error == GRPC_ERROR_NONE) { if (calld->security_context_set == 0 && !op->cancel_stream) {
calld->security_context_set = 1; calld->security_context_set = 1;
GPR_ASSERT(op->context); GPR_ASSERT(op->payload->context != NULL);
if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) { if (op->payload->context[GRPC_CONTEXT_SECURITY].value == NULL) {
op->context[GRPC_CONTEXT_SECURITY].value = op->payload->context[GRPC_CONTEXT_SECURITY].value =
grpc_client_security_context_create(); grpc_client_security_context_create();
op->context[GRPC_CONTEXT_SECURITY].destroy = op->payload->context[GRPC_CONTEXT_SECURITY].destroy =
grpc_client_security_context_destroy; grpc_client_security_context_destroy;
} }
sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value; sec_ctx = op->payload->context[GRPC_CONTEXT_SECURITY].value;
GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter");
sec_ctx->auth_context = sec_ctx->auth_context =
GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter"); GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter");
} }
if (op->send_initial_metadata != NULL) { if (op->send_initial_metadata) {
for (l = op->send_initial_metadata->list.head; l != NULL; l = l->next) { for (l = op->payload->send_initial_metadata.send_initial_metadata->list
.head;
l != NULL; l = l->next) {
grpc_mdelem md = l->md; grpc_mdelem md = l->md;
/* Pointer comparison is OK for md_elems created from the same context. /* Pointer comparison is OK for md_elems created from the same context.
*/ */

@ -49,7 +49,7 @@ typedef struct call_data {
up-call on transport_op, and remember to call our on_done_recv member after up-call on transport_op, and remember to call our on_done_recv member after
handling it. */ handling it. */
grpc_closure auth_on_recv; grpc_closure auth_on_recv;
grpc_transport_stream_op *transport_op; grpc_transport_stream_op_batch *transport_op;
grpc_metadata_array md; grpc_metadata_array md;
const grpc_metadata *consumed_md; const grpc_metadata *consumed_md;
size_t num_consumed_md; size_t num_consumed_md;
@ -138,12 +138,11 @@ static void on_md_processing_done(
error_details = error_details != NULL error_details = error_details != NULL
? error_details ? error_details
: "Authentication metadata processing failed."; : "Authentication metadata processing failed.";
calld->transport_op->send_initial_metadata = NULL; if (calld->transport_op->send_message) {
if (calld->transport_op->send_message != NULL) { grpc_byte_stream_destroy(
grpc_byte_stream_destroy(&exec_ctx, calld->transport_op->send_message); &exec_ctx, calld->transport_op->payload->send_message.send_message);
calld->transport_op->send_message = NULL; calld->transport_op->payload->send_message.send_message = NULL;
} }
calld->transport_op->send_trailing_metadata = NULL;
grpc_closure_sched( grpc_closure_sched(
&exec_ctx, calld->on_done_recv, &exec_ctx, calld->on_done_recv,
grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details),
@ -171,14 +170,17 @@ static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
} }
static void set_recv_ops_md_callbacks(grpc_call_element *elem, static void set_recv_ops_md_callbacks(grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
/* substitute our callback for the higher callback */ /* substitute our callback for the higher callback */
calld->recv_initial_metadata = op->recv_initial_metadata; calld->recv_initial_metadata =
calld->on_done_recv = op->recv_initial_metadata_ready; op->payload->recv_initial_metadata.recv_initial_metadata;
op->recv_initial_metadata_ready = &calld->auth_on_recv; calld->on_done_recv =
op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->auth_on_recv;
calld->transport_op = op; calld->transport_op = op;
} }
} }
@ -190,7 +192,7 @@ static void set_recv_ops_md_callbacks(grpc_call_element *elem,
that is being sent or received. */ that is being sent or received. */
static void auth_start_transport_op(grpc_exec_ctx *exec_ctx, static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
set_recv_ops_md_callbacks(elem, op); set_recv_ops_md_callbacks(elem, op);
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
} }

@ -117,25 +117,32 @@ static received_status unpack_received_status(gpr_atm atm) {
typedef struct batch_control { typedef struct batch_control {
grpc_call *call; grpc_call *call;
grpc_cq_completion cq_completion; /* Share memory for cq_completion and notify_tag as they are never needed
simultaneously. Each byte used in this data structure count as six bytes
per call, so any savings we can make are worthwhile,
We use notify_tag to determine whether or not to send notification to the
completion queue. Once we've made that determination, we can reuse the
memory for cq_completion. */
union {
grpc_cq_completion cq_completion;
struct {
/* Any given op indicates completion by either (a) calling a closure or
(b) sending a notification on the call's completion queue. If
\a is_closure is true, \a tag indicates a closure to be invoked;
otherwise, \a tag indicates the tag to be used in the notification to
be sent to the completion queue. */
void *tag;
bool is_closure;
} notify_tag;
} completion_data;
grpc_closure finish_batch; grpc_closure finish_batch;
void *notify_tag;
gpr_refcount steps_to_complete; gpr_refcount steps_to_complete;
grpc_error *errors[MAX_ERRORS_PER_BATCH]; grpc_error *errors[MAX_ERRORS_PER_BATCH];
gpr_atm num_errors; gpr_atm num_errors;
uint8_t send_initial_metadata; grpc_transport_stream_op_batch op;
uint8_t send_message;
uint8_t send_final_op;
uint8_t recv_initial_metadata;
uint8_t recv_message;
uint8_t recv_final_op;
uint8_t is_notify_tag_closure;
/* TODO(ctiller): now that this is inlined, figure out how much of the above
state can be eliminated */
grpc_transport_stream_op op;
} batch_control; } batch_control;
struct grpc_call { struct grpc_call {
@ -169,6 +176,7 @@ struct grpc_call {
bool has_initial_md_been_received; bool has_initial_md_been_received;
batch_control active_batches[MAX_CONCURRENT_BATCHES]; batch_control active_batches[MAX_CONCURRENT_BATCHES];
grpc_transport_stream_op_batch_payload stream_op_payload;
/* first idx: is_receiving, second idx: is_trailing */ /* first idx: is_receiving, second idx: is_trailing */
grpc_metadata_batch metadata_batch[2][2]; grpc_metadata_batch metadata_batch[2][2];
@ -239,7 +247,7 @@ int grpc_call_error_trace = 0;
CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem))
static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
grpc_transport_stream_op *op); grpc_transport_stream_op_batch *op);
static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c,
status_source source, grpc_status_code status, status_source source, grpc_status_code status,
const char *description); const char *description);
@ -291,6 +299,7 @@ grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx,
/* Always support no compression */ /* Always support no compression */
GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE);
call->is_client = args->server_transport_data == NULL; call->is_client = args->server_transport_data == NULL;
call->stream_op_payload.context = call->context;
grpc_slice path = grpc_empty_slice(); grpc_slice path = grpc_empty_slice();
if (call->is_client) { if (call->is_client) {
GPR_ASSERT(args->add_initial_metadata_count < GPR_ASSERT(args->add_initial_metadata_count <
@ -535,13 +544,12 @@ grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) {
} }
static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
grpc_call_element *elem; grpc_call_element *elem;
GPR_TIMER_BEGIN("execute_op", 0); GPR_TIMER_BEGIN("execute_op", 0);
elem = CALL_ELEM_FROM_CALL(call, 0); elem = CALL_ELEM_FROM_CALL(call, 0);
op->context = call->context; elem->filter->start_transport_stream_op_batch(exec_ctx, elem, op);
elem->filter->start_transport_stream_op(exec_ctx, elem, op);
GPR_TIMER_END("execute_op", 0); GPR_TIMER_END("execute_op", 0);
} }
@ -594,9 +602,10 @@ static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c,
status_source source, grpc_error *error) { status_source source, grpc_error *error) {
GRPC_CALL_INTERNAL_REF(c, "termination"); GRPC_CALL_INTERNAL_REF(c, "termination");
set_status_from_error(exec_ctx, c, source, GRPC_ERROR_REF(error)); set_status_from_error(exec_ctx, c, source, GRPC_ERROR_REF(error));
grpc_transport_stream_op *op = grpc_make_transport_stream_op( grpc_transport_stream_op_batch *op = grpc_make_transport_stream_op(
grpc_closure_create(done_termination, c, grpc_schedule_on_exec_ctx)); grpc_closure_create(done_termination, c, grpc_schedule_on_exec_ctx));
op->cancel_error = error; op->cancel_stream = true;
op->payload->cancel_stream.cancel_error = error;
execute_op(exec_ctx, c, op); execute_op(exec_ctx, c, op);
} }
@ -1025,16 +1034,13 @@ static batch_control *allocate_batch_control(grpc_call *call,
const grpc_op *ops, const grpc_op *ops,
size_t num_ops) { size_t num_ops) {
int slot = batch_slot_for_op(ops[0].op); int slot = batch_slot_for_op(ops[0].op);
for (size_t i = 1; i < num_ops; i++) {
int op_slot = batch_slot_for_op(ops[i].op);
slot = GPR_MIN(slot, op_slot);
}
batch_control *bctl = &call->active_batches[slot]; batch_control *bctl = &call->active_batches[slot];
if (bctl->call != NULL) { if (bctl->call != NULL) {
return NULL; return NULL;
} }
memset(bctl, 0, sizeof(*bctl)); memset(bctl, 0, sizeof(*bctl));
bctl->call = call; bctl->call = call;
bctl->op.payload = &call->stream_op_payload;
return bctl; return bctl;
} }
@ -1074,20 +1080,20 @@ static void post_batch_completion(grpc_exec_ctx *exec_ctx,
grpc_call *call = bctl->call; grpc_call *call = bctl->call;
grpc_error *error = consolidate_batch_errors(bctl); grpc_error *error = consolidate_batch_errors(bctl);
if (bctl->send_initial_metadata) { if (bctl->op.send_initial_metadata) {
grpc_metadata_batch_destroy( grpc_metadata_batch_destroy(
exec_ctx, exec_ctx,
&call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]);
} }
if (bctl->send_message) { if (bctl->op.send_message) {
call->sending_message = false; call->sending_message = false;
} }
if (bctl->send_final_op) { if (bctl->op.send_trailing_metadata) {
grpc_metadata_batch_destroy( grpc_metadata_batch_destroy(
exec_ctx, exec_ctx,
&call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]);
} }
if (bctl->recv_final_op) { if (bctl->op.recv_trailing_metadata) {
grpc_metadata_batch *md = grpc_metadata_batch *md =
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
recv_trailing_filter(exec_ctx, call, md); recv_trailing_filter(exec_ctx, call, md);
@ -1123,15 +1129,16 @@ static void post_batch_completion(grpc_exec_ctx *exec_ctx,
error = GRPC_ERROR_NONE; error = GRPC_ERROR_NONE;
} }
if (bctl->is_notify_tag_closure) { if (bctl->completion_data.notify_tag.is_closure) {
/* unrefs bctl->error */ /* unrefs bctl->error */
bctl->call = NULL; bctl->call = NULL;
grpc_closure_run(exec_ctx, bctl->notify_tag, error); grpc_closure_run(exec_ctx, bctl->completion_data.notify_tag.tag, error);
GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion");
} else { } else {
/* unrefs bctl->error */ /* unrefs bctl->error */
grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, error, grpc_cq_end_op(
finish_batch_completion, bctl, &bctl->cq_completion); exec_ctx, bctl->call->cq, bctl->completion_data.notify_tag.tag, error,
finish_batch_completion, bctl, &bctl->completion_data.cq_completion);
} }
} }
@ -1374,11 +1381,13 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
if (bctl == NULL) { if (bctl == NULL) {
return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
} }
bctl->notify_tag = notify_tag; bctl->completion_data.notify_tag.tag = notify_tag;
bctl->is_notify_tag_closure = (uint8_t)(is_notify_tag_closure != 0); bctl->completion_data.notify_tag.is_closure =
(uint8_t)(is_notify_tag_closure != 0);
grpc_transport_stream_op *stream_op = &bctl->op; grpc_transport_stream_op_batch *stream_op = &bctl->op;
memset(stream_op, 0, sizeof(*stream_op)); grpc_transport_stream_op_batch_payload *stream_op_payload =
&call->stream_op_payload;
stream_op->covered_by_poller = true; stream_op->covered_by_poller = true;
/* rewrite batch ops into a transport op */ /* rewrite batch ops into a transport op */
@ -1432,8 +1441,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_INVALID_METADATA; error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error; goto done_with_error;
} }
bctl->send_initial_metadata = 1; stream_op->send_initial_metadata = true;
call->sent_initial_metadata = 1; call->sent_initial_metadata = true;
if (!prepare_application_metadata( if (!prepare_application_metadata(
exec_ctx, call, (int)op->data.send_initial_metadata.count, exec_ctx, call, (int)op->data.send_initial_metadata.count,
op->data.send_initial_metadata.metadata, 0, call->is_client, op->data.send_initial_metadata.metadata, 0, call->is_client,
@ -1443,9 +1452,10 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
} }
/* TODO(ctiller): just make these the same variable? */ /* TODO(ctiller): just make these the same variable? */
call->metadata_batch[0][0].deadline = call->send_deadline; call->metadata_batch[0][0].deadline = call->send_deadline;
stream_op->send_initial_metadata = stream_op_payload->send_initial_metadata.send_initial_metadata =
&call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */];
stream_op->send_initial_metadata_flags = op->flags; stream_op_payload->send_initial_metadata.send_initial_metadata_flags =
op->flags;
break; break;
case GRPC_OP_SEND_MESSAGE: case GRPC_OP_SEND_MESSAGE:
if (!are_write_flags_valid(op->flags)) { if (!are_write_flags_valid(op->flags)) {
@ -1460,8 +1470,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error; goto done_with_error;
} }
bctl->send_message = 1; stream_op->send_message = true;
call->sending_message = 1; call->sending_message = true;
grpc_slice_buffer_stream_init( grpc_slice_buffer_stream_init(
&call->sending_stream, &call->sending_stream,
&op->data.send_message.send_message->data.raw.slice_buffer, &op->data.send_message.send_message->data.raw.slice_buffer,
@ -1473,7 +1483,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
GRPC_COMPRESS_NONE) { GRPC_COMPRESS_NONE) {
call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS;
} }
stream_op->send_message = &call->sending_stream.base; stream_op_payload->send_message.send_message =
&call->sending_stream.base;
break; break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT: case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Flag validation: currently allow no flags */ /* Flag validation: currently allow no flags */
@ -1489,9 +1500,9 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error; goto done_with_error;
} }
bctl->send_final_op = 1; stream_op->send_trailing_metadata = true;
call->sent_final_op = 1; call->sent_final_op = true;
stream_op->send_trailing_metadata = stream_op_payload->send_trailing_metadata.send_trailing_metadata =
&call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
break; break;
case GRPC_OP_SEND_STATUS_FROM_SERVER: case GRPC_OP_SEND_STATUS_FROM_SERVER:
@ -1513,8 +1524,8 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_INVALID_METADATA; error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error; goto done_with_error;
} }
bctl->send_final_op = 1; stream_op->send_trailing_metadata = true;
call->sent_final_op = 1; call->sent_final_op = true;
GPR_ASSERT(call->send_extra_metadata_count == 0); GPR_ASSERT(call->send_extra_metadata_count == 0);
call->send_extra_metadata_count = 1; call->send_extra_metadata_count = 1;
call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem(
@ -1553,7 +1564,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_INVALID_METADATA; error = GRPC_CALL_ERROR_INVALID_METADATA;
goto done_with_error; goto done_with_error;
} }
stream_op->send_trailing_metadata = stream_op_payload->send_trailing_metadata.send_trailing_metadata =
&call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */];
break; break;
case GRPC_OP_RECV_INITIAL_METADATA: case GRPC_OP_RECV_INITIAL_METADATA:
@ -1570,16 +1581,16 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
from server.c. In that case, it's coming from accept_stream, and in from server.c. In that case, it's coming from accept_stream, and in
that case we're not necessarily covered by a poller. */ that case we're not necessarily covered by a poller. */
stream_op->covered_by_poller = call->is_client; stream_op->covered_by_poller = call->is_client;
call->received_initial_metadata = 1; call->received_initial_metadata = true;
call->buffered_metadata[0] = call->buffered_metadata[0] =
op->data.recv_initial_metadata.recv_initial_metadata; op->data.recv_initial_metadata.recv_initial_metadata;
grpc_closure_init(&call->receiving_initial_metadata_ready, grpc_closure_init(&call->receiving_initial_metadata_ready,
receiving_initial_metadata_ready, bctl, receiving_initial_metadata_ready, bctl,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
bctl->recv_initial_metadata = 1; stream_op->recv_initial_metadata = true;
stream_op->recv_initial_metadata = stream_op_payload->recv_initial_metadata.recv_initial_metadata =
&call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
stream_op->recv_initial_metadata_ready = stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready =
&call->receiving_initial_metadata_ready; &call->receiving_initial_metadata_ready;
num_completion_callbacks_needed++; num_completion_callbacks_needed++;
break; break;
@ -1593,13 +1604,14 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error; goto done_with_error;
} }
call->receiving_message = 1; call->receiving_message = true;
bctl->recv_message = 1; stream_op->recv_message = true;
call->receiving_buffer = op->data.recv_message.recv_message; call->receiving_buffer = op->data.recv_message.recv_message;
stream_op->recv_message = &call->receiving_stream; stream_op_payload->recv_message.recv_message = &call->receiving_stream;
grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready, grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready,
bctl, grpc_schedule_on_exec_ctx); bctl, grpc_schedule_on_exec_ctx);
stream_op->recv_message_ready = &call->receiving_stream_ready; stream_op_payload->recv_message.recv_message_ready =
&call->receiving_stream_ready;
num_completion_callbacks_needed++; num_completion_callbacks_needed++;
break; break;
case GRPC_OP_RECV_STATUS_ON_CLIENT: case GRPC_OP_RECV_STATUS_ON_CLIENT:
@ -1616,16 +1628,17 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error; goto done_with_error;
} }
call->requested_final_op = 1; call->requested_final_op = true;
call->buffered_metadata[1] = call->buffered_metadata[1] =
op->data.recv_status_on_client.trailing_metadata; 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 = op->data.recv_status_on_client.status;
call->final_op.client.status_details = call->final_op.client.status_details =
op->data.recv_status_on_client.status_details; op->data.recv_status_on_client.status_details;
bctl->recv_final_op = 1; stream_op->recv_trailing_metadata = true;
stream_op->recv_trailing_metadata = stream_op->collect_stats = true;
stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
stream_op->collect_stats = stream_op_payload->collect_stats.collect_stats =
&call->final_info.stats.transport_stream_stats; &call->final_info.stats.transport_stream_stats;
break; break;
case GRPC_OP_RECV_CLOSE_ON_SERVER: case GRPC_OP_RECV_CLOSE_ON_SERVER:
@ -1642,13 +1655,14 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS;
goto done_with_error; goto done_with_error;
} }
call->requested_final_op = 1; call->requested_final_op = true;
call->final_op.server.cancelled = call->final_op.server.cancelled =
op->data.recv_close_on_server.cancelled; op->data.recv_close_on_server.cancelled;
bctl->recv_final_op = 1; stream_op->recv_trailing_metadata = true;
stream_op->recv_trailing_metadata = stream_op->collect_stats = true;
stream_op_payload->recv_trailing_metadata.recv_trailing_metadata =
&call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */];
stream_op->collect_stats = stream_op_payload->collect_stats.collect_stats =
&call->final_info.stats.transport_stream_stats; &call->final_info.stats.transport_stream_stats;
break; break;
} }
@ -1660,7 +1674,6 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
} }
gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); 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, grpc_closure_init(&bctl->finish_batch, finish_batch, bctl,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
stream_op->on_complete = &bctl->finish_batch; stream_op->on_complete = &bctl->finish_batch;
@ -1674,26 +1687,26 @@ done:
done_with_error: done_with_error:
/* reverse any mutations that occured */ /* reverse any mutations that occured */
if (bctl->send_initial_metadata) { if (stream_op->send_initial_metadata) {
call->sent_initial_metadata = 0; call->sent_initial_metadata = false;
grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][0]); grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][0]);
} }
if (bctl->send_message) { if (stream_op->send_message) {
call->sending_message = 0; call->sending_message = false;
grpc_byte_stream_destroy(exec_ctx, &call->sending_stream.base); grpc_byte_stream_destroy(exec_ctx, &call->sending_stream.base);
} }
if (bctl->send_final_op) { if (stream_op->send_trailing_metadata) {
call->sent_final_op = 0; call->sent_final_op = false;
grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][1]); grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][1]);
} }
if (bctl->recv_initial_metadata) { if (stream_op->recv_initial_metadata) {
call->received_initial_metadata = 0; call->received_initial_metadata = false;
} }
if (bctl->recv_message) { if (stream_op->recv_message) {
call->receiving_message = 0; call->receiving_message = false;
} }
if (bctl->recv_final_op) { if (stream_op->recv_trailing_metadata) {
call->requested_final_op = 0; call->requested_final_op = false;
} }
goto done; goto done;
} }

@ -80,16 +80,18 @@ static void fill_metadata(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
mdb->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); mdb->deadline = gpr_inf_future(GPR_CLOCK_REALTIME);
} }
static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void lame_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
fill_metadata(exec_ctx, elem, op->recv_initial_metadata); fill_metadata(exec_ctx, elem,
} else if (op->recv_trailing_metadata != NULL) { op->payload->recv_initial_metadata.recv_initial_metadata);
fill_metadata(exec_ctx, elem, op->recv_trailing_metadata); } else if (op->recv_trailing_metadata) {
fill_metadata(exec_ctx, elem,
op->payload->recv_trailing_metadata.recv_trailing_metadata);
} }
grpc_transport_stream_op_finish_with_failure( grpc_transport_stream_op_batch_finish_with_failure(
exec_ctx, op, exec_ctx, op,
GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel")); GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel"));
} }
@ -148,7 +150,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem) {} grpc_channel_element *elem) {}
const grpc_channel_filter grpc_lame_filter = { const grpc_channel_filter grpc_lame_filter = {
lame_start_transport_stream_op, lame_start_transport_stream_op_batch,
lame_start_transport_op, lame_start_transport_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -154,8 +154,7 @@ struct call_data {
grpc_completion_queue *cq_new; grpc_completion_queue *cq_new;
grpc_metadata_batch *recv_initial_metadata; grpc_metadata_batch *recv_initial_metadata;
bool recv_idempotent_request; uint32_t recv_initial_metadata_flags;
bool recv_cacheable_request;
grpc_metadata_array initial_metadata; grpc_metadata_array initial_metadata;
request_matcher *request_matcher; request_matcher *request_matcher;
@ -498,13 +497,7 @@ static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
rc->data.batch.details->host = grpc_slice_ref_internal(calld->host); rc->data.batch.details->host = grpc_slice_ref_internal(calld->host);
rc->data.batch.details->method = grpc_slice_ref_internal(calld->path); rc->data.batch.details->method = grpc_slice_ref_internal(calld->path);
rc->data.batch.details->deadline = calld->deadline; rc->data.batch.details->deadline = calld->deadline;
rc->data.batch.details->flags = rc->data.batch.details->flags = calld->recv_initial_metadata_flags;
(calld->recv_idempotent_request
? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST
: 0) |
(calld->recv_cacheable_request
? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST
: 0);
break; break;
case REGISTERED_CALL: case REGISTERED_CALL:
*rc->data.registered.deadline = calld->deadline; *rc->data.registered.deadline = calld->deadline;
@ -632,7 +625,8 @@ static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
if (!grpc_slice_eq(rm->host, calld->host)) continue; if (!grpc_slice_eq(rm->host, calld->host)) continue;
if (!grpc_slice_eq(rm->method, calld->path)) continue; if (!grpc_slice_eq(rm->method, calld->path)) continue;
if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
!calld->recv_idempotent_request) { 0 == (calld->recv_initial_metadata_flags &
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
continue; continue;
} }
finish_start_new_rpc(exec_ctx, server, elem, finish_start_new_rpc(exec_ctx, server, elem,
@ -649,7 +643,8 @@ static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
if (rm->has_host) continue; if (rm->has_host) continue;
if (!grpc_slice_eq(rm->method, calld->path)) continue; if (!grpc_slice_eq(rm->method, calld->path)) continue;
if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) &&
!calld->recv_idempotent_request) { 0 == (calld->recv_initial_metadata_flags &
GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) {
continue; continue;
} }
finish_start_new_rpc(exec_ctx, server, elem, finish_start_new_rpc(exec_ctx, server, elem,
@ -781,22 +776,25 @@ static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
} }
static void server_mutate_op(grpc_call_element *elem, static void server_mutate_op(grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
GPR_ASSERT(op->recv_idempotent_request == NULL); GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == NULL);
calld->recv_initial_metadata = op->recv_initial_metadata; calld->recv_initial_metadata =
calld->on_done_recv_initial_metadata = op->recv_initial_metadata_ready; op->payload->recv_initial_metadata.recv_initial_metadata;
op->recv_initial_metadata_ready = &calld->server_on_recv_initial_metadata; calld->on_done_recv_initial_metadata =
op->recv_idempotent_request = &calld->recv_idempotent_request; op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->recv_cacheable_request = &calld->recv_cacheable_request; op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->server_on_recv_initial_metadata;
op->payload->recv_initial_metadata.recv_flags =
&calld->recv_initial_metadata_flags;
} }
} }
static void server_start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void server_start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GRPC_CALL_LOG_OP(GPR_INFO, elem, op);
server_mutate_op(elem, op); server_mutate_op(elem, op);
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
@ -960,7 +958,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
} }
const grpc_channel_filter grpc_server_top_filter = { const grpc_channel_filter grpc_server_top_filter = {
server_start_transport_stream_op, server_start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -170,7 +170,7 @@ int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx,
void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx,
grpc_transport *transport, grpc_transport *transport,
grpc_stream *stream, grpc_stream *stream,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
transport->vtable->perform_stream_op(exec_ctx, transport, stream, op); transport->vtable->perform_stream_op(exec_ctx, transport, stream, op);
} }
@ -213,14 +213,23 @@ grpc_endpoint *grpc_transport_get_endpoint(grpc_exec_ctx *exec_ctx,
return transport->vtable->get_endpoint(exec_ctx, transport); return transport->vtable->get_endpoint(exec_ctx, transport);
} }
void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx, void grpc_transport_stream_op_batch_finish_with_failure(
grpc_transport_stream_op *op, grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *op,
grpc_error *error) { grpc_error *error) {
grpc_closure_sched(exec_ctx, op->recv_message_ready, GRPC_ERROR_REF(error)); if (op->recv_message) {
grpc_closure_sched(exec_ctx, op->recv_initial_metadata_ready, grpc_closure_sched(exec_ctx, op->payload->recv_message.recv_message_ready,
GRPC_ERROR_REF(error)); GRPC_ERROR_REF(error));
}
if (op->recv_initial_metadata) {
grpc_closure_sched(
exec_ctx,
op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_REF(error));
}
grpc_closure_sched(exec_ctx, op->on_complete, error); grpc_closure_sched(exec_ctx, op->on_complete, error);
GRPC_ERROR_UNREF(op->cancel_error); if (op->cancel_stream) {
GRPC_ERROR_UNREF(op->payload->cancel_stream.cancel_error);
}
} }
typedef struct { typedef struct {
@ -249,7 +258,8 @@ grpc_transport_op *grpc_make_transport_op(grpc_closure *on_complete) {
typedef struct { typedef struct {
grpc_closure outer_on_complete; grpc_closure outer_on_complete;
grpc_closure *inner_on_complete; grpc_closure *inner_on_complete;
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
grpc_transport_stream_op_batch_payload payload;
} made_transport_stream_op; } made_transport_stream_op;
static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg, static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg,
@ -260,13 +270,13 @@ static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg,
grpc_closure_run(exec_ctx, c, GRPC_ERROR_REF(error)); grpc_closure_run(exec_ctx, c, GRPC_ERROR_REF(error));
} }
grpc_transport_stream_op *grpc_make_transport_stream_op( grpc_transport_stream_op_batch *grpc_make_transport_stream_op(
grpc_closure *on_complete) { grpc_closure *on_complete) {
made_transport_stream_op *op = gpr_malloc(sizeof(*op)); made_transport_stream_op *op = gpr_zalloc(sizeof(*op));
op->op.payload = &op->payload;
grpc_closure_init(&op->outer_on_complete, destroy_made_transport_stream_op, grpc_closure_init(&op->outer_on_complete, destroy_made_transport_stream_op,
op, grpc_schedule_on_exec_ctx); op, grpc_schedule_on_exec_ctx);
op->inner_on_complete = on_complete; op->inner_on_complete = on_complete;
memset(&op->op, 0, sizeof(op->op));
op->op.on_complete = &op->outer_on_complete; op->op.on_complete = &op->outer_on_complete;
return &op->op; return &op->op;
} }

@ -109,55 +109,98 @@ void grpc_transport_move_stats(grpc_transport_stream_stats *from,
grpc_transport_stream_stats *to); grpc_transport_stream_stats *to);
typedef struct { typedef struct {
void *extra_arg;
grpc_closure closure; grpc_closure closure;
void *args[2]; } grpc_handler_private_op_data;
} grpc_transport_private_op_data;
typedef struct grpc_transport_stream_op_batch_payload
grpc_transport_stream_op_batch_payload;
/* Transport stream op: a set of operations to perform on a transport /* Transport stream op: a set of operations to perform on a transport
against a single stream */ against a single stream */
typedef struct grpc_transport_stream_op { typedef struct grpc_transport_stream_op_batch {
/** Should be enqueued when all requested operations (excluding recv_message /** Should be enqueued when all requested operations (excluding recv_message
and recv_initial_metadata which have their own closures) in a given batch and recv_initial_metadata which have their own closures) in a given batch
have been completed. */ have been completed. */
grpc_closure *on_complete; grpc_closure *on_complete;
/** Values for the stream op (fields set are determined by flags above) */
grpc_transport_stream_op_batch_payload *payload;
/** Is the completion of this op covered by a poller (if false: the op should /** Is the completion of this op covered by a poller (if false: the op should
complete independently of some pollset being polled) */ complete independently of some pollset being polled) */
bool covered_by_poller; bool covered_by_poller : 1;
/** Send initial metadata to the peer, from the provided metadata batch. /** Send initial metadata to the peer, from the provided metadata batch. */
idempotent_request MUST be set if this is non-null */ bool send_initial_metadata : 1;
grpc_metadata_batch *send_initial_metadata;
/** Iff send_initial_metadata != NULL, flags associated with
send_initial_metadata: a bitfield of GRPC_INITIAL_METADATA_xxx */
uint32_t send_initial_metadata_flags;
/** Send trailing metadata to the peer, from the provided metadata batch. */ /** Send trailing metadata to the peer, from the provided metadata batch. */
grpc_metadata_batch *send_trailing_metadata; bool send_trailing_metadata : 1;
/** Send message data to the peer, from the provided byte stream. */ /** Send message data to the peer, from the provided byte stream. */
grpc_byte_stream *send_message; bool send_message : 1;
/** Receive initial metadata from the stream, into provided metadata batch. */ /** Receive initial metadata from the stream, into provided metadata batch. */
grpc_metadata_batch *recv_initial_metadata; bool recv_initial_metadata : 1;
bool *recv_idempotent_request;
bool *recv_cacheable_request;
/** Should be enqueued when initial metadata is ready to be processed. */
grpc_closure *recv_initial_metadata_ready;
/** Receive message data from the stream, into provided byte stream. */ /** Receive message data from the stream, into provided byte stream. */
grpc_byte_stream **recv_message; bool recv_message : 1;
/** Should be enqueued when one message is ready to be processed. */
grpc_closure *recv_message_ready;
/** Receive trailing metadata from the stream, into provided metadata batch. /** Receive trailing metadata from the stream, into provided metadata batch.
*/ */
grpc_metadata_batch *recv_trailing_metadata; bool recv_trailing_metadata : 1;
/** Collect any stats into provided buffer, zero internal stat counters */ /** Collect any stats into provided buffer, zero internal stat counters */
grpc_transport_stream_stats *collect_stats; bool collect_stats : 1;
/** Cancel this stream with the provided error */
bool cancel_stream : 1;
/***************************************************************************
* remaining fields are initialized and used at the discretion of the
* current handler of the op */
/** If != GRPC_ERROR_NONE, forcefully close this stream. grpc_handler_private_op_data handler_private;
} grpc_transport_stream_op_batch;
struct grpc_transport_stream_op_batch_payload {
struct {
grpc_metadata_batch *send_initial_metadata;
/** Iff send_initial_metadata != NULL, flags associated with
send_initial_metadata: a bitfield of GRPC_INITIAL_METADATA_xxx */
uint32_t send_initial_metadata_flags;
} send_initial_metadata;
struct {
grpc_metadata_batch *send_trailing_metadata;
} send_trailing_metadata;
struct {
grpc_byte_stream *send_message;
} send_message;
struct {
grpc_metadata_batch *recv_initial_metadata;
uint32_t *recv_flags;
/** Should be enqueued when initial metadata is ready to be processed. */
grpc_closure *recv_initial_metadata_ready;
} recv_initial_metadata;
struct {
grpc_byte_stream **recv_message;
/** Should be enqueued when one message is ready to be processed. */
grpc_closure *recv_message_ready;
} recv_message;
struct {
grpc_metadata_batch *recv_trailing_metadata;
} recv_trailing_metadata;
struct {
grpc_transport_stream_stats *collect_stats;
} collect_stats;
/** Forcefully close this stream.
The HTTP2 semantics should be: The HTTP2 semantics should be:
- server side: if cancel_error has GRPC_ERROR_INT_GRPC_STATUS, and - server side: if cancel_error has GRPC_ERROR_INT_GRPC_STATUS, and
trailing metadata has not been sent, send trailing metadata with status trailing metadata has not been sent, send trailing metadata with status
@ -167,17 +210,13 @@ typedef struct grpc_transport_stream_op {
convert to a HTTP2 error code using convert to a HTTP2 error code using
grpc_chttp2_grpc_status_to_http2_error. Send a RST_STREAM with this grpc_chttp2_grpc_status_to_http2_error. Send a RST_STREAM with this
error. */ error. */
grpc_error *cancel_error; struct {
grpc_error *cancel_error;
} cancel_stream;
/* Indexes correspond to grpc_context_index enum values */ /* Indexes correspond to grpc_context_index enum values */
grpc_call_context_element *context; grpc_call_context_element *context;
};
/***************************************************************************
* remaining fields are initialized and used at the discretion of the
* current handler of the op */
grpc_transport_private_op_data handler_private;
} grpc_transport_stream_op;
/** Transport op: a set of operations to perform on a transport as a whole */ /** Transport op: a set of operations to perform on a transport as a whole */
typedef struct grpc_transport_op { typedef struct grpc_transport_op {
@ -210,7 +249,7 @@ typedef struct grpc_transport_op {
* remaining fields are initialized and used at the discretion of the * remaining fields are initialized and used at the discretion of the
* transport implementation */ * transport implementation */
grpc_transport_private_op_data transport_private; grpc_handler_private_op_data handler_private;
} grpc_transport_op; } grpc_transport_op;
/* Returns the amount of memory required to store a grpc_stream for this /* Returns the amount of memory required to store a grpc_stream for this
@ -250,11 +289,11 @@ void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx,
grpc_stream *stream, grpc_stream *stream,
grpc_closure *then_schedule_closure); grpc_closure *then_schedule_closure);
void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx, void grpc_transport_stream_op_batch_finish_with_failure(
grpc_transport_stream_op *op, grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *op,
grpc_error *error); grpc_error *error);
char *grpc_transport_stream_op_string(grpc_transport_stream_op *op); char *grpc_transport_stream_op_batch_string(grpc_transport_stream_op_batch *op);
char *grpc_transport_op_string(grpc_transport_op *op); char *grpc_transport_op_string(grpc_transport_op *op);
/* Send a batch of operations on a transport /* Send a batch of operations on a transport
@ -265,11 +304,12 @@ char *grpc_transport_op_string(grpc_transport_op *op);
transport - the transport on which to initiate the stream transport - the transport on which to initiate the stream
stream - the stream on which to send the operations. This must be stream - the stream on which to send the operations. This must be
non-NULL and previously initialized by the same transport. non-NULL and previously initialized by the same transport.
op - a grpc_transport_stream_op specifying the op to perform */ op - a grpc_transport_stream_op_batch specifying the op to perform
*/
void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx,
grpc_transport *transport, grpc_transport *transport,
grpc_stream *stream, grpc_stream *stream,
grpc_transport_stream_op *op); grpc_transport_stream_op_batch *op);
void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx,
grpc_transport *transport, grpc_transport *transport,
@ -301,9 +341,10 @@ grpc_endpoint *grpc_transport_get_endpoint(grpc_exec_ctx *exec_ctx,
/* Allocate a grpc_transport_op, and preconfigure the on_consumed closure to /* Allocate a grpc_transport_op, and preconfigure the on_consumed closure to
\a on_consumed and then delete the returned transport op */ \a on_consumed and then delete the returned transport op */
grpc_transport_op *grpc_make_transport_op(grpc_closure *on_consumed); grpc_transport_op *grpc_make_transport_op(grpc_closure *on_consumed);
/* Allocate a grpc_transport_stream_op, and preconfigure the on_consumed closure /* Allocate a grpc_transport_stream_op_batch, and preconfigure the on_consumed
closure
to \a on_consumed and then delete the returned transport op */ to \a on_consumed and then delete the returned transport op */
grpc_transport_stream_op *grpc_make_transport_stream_op( grpc_transport_stream_op_batch *grpc_make_transport_stream_op(
grpc_closure *on_consumed); grpc_closure *on_consumed);
#ifdef __cplusplus #ifdef __cplusplus

@ -59,7 +59,8 @@ typedef struct grpc_transport_vtable {
/* implementation of grpc_transport_perform_stream_op */ /* implementation of grpc_transport_perform_stream_op */
void (*perform_stream_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, void (*perform_stream_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self,
grpc_stream *stream, grpc_transport_stream_op *op); grpc_stream *stream,
grpc_transport_stream_op_batch *op);
/* implementation of grpc_transport_perform_op */ /* implementation of grpc_transport_perform_op */
void (*perform_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, void (*perform_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self,

@ -71,7 +71,8 @@ static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) {
} }
} }
char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) { char *grpc_transport_stream_op_batch_string(
grpc_transport_stream_op_batch *op) {
char *tmp; char *tmp;
char *out; char *out;
@ -81,45 +82,49 @@ char *grpc_transport_stream_op_string(grpc_transport_stream_op *op) {
gpr_strvec_add( gpr_strvec_add(
&b, gpr_strdup(op->covered_by_poller ? "[COVERED]" : "[UNCOVERED]")); &b, gpr_strdup(op->covered_by_poller ? "[COVERED]" : "[UNCOVERED]"));
if (op->send_initial_metadata != NULL) { if (op->send_initial_metadata) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{")); gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{"));
put_metadata_list(&b, *op->send_initial_metadata); put_metadata_list(
&b, *op->payload->send_initial_metadata.send_initial_metadata);
gpr_strvec_add(&b, gpr_strdup("}")); gpr_strvec_add(&b, gpr_strdup("}"));
} }
if (op->send_message != NULL) { if (op->send_message) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d",
op->send_message->flags, op->send_message->length); op->payload->send_message.send_message->flags,
op->payload->send_message.send_message->length);
gpr_strvec_add(&b, tmp); gpr_strvec_add(&b, tmp);
} }
if (op->send_trailing_metadata != NULL) { if (op->send_trailing_metadata) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{")); gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{"));
put_metadata_list(&b, *op->send_trailing_metadata); put_metadata_list(
&b, *op->payload->send_trailing_metadata.send_trailing_metadata);
gpr_strvec_add(&b, gpr_strdup("}")); gpr_strvec_add(&b, gpr_strdup("}"));
} }
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA")); gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA"));
} }
if (op->recv_message != NULL) { if (op->recv_message) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE"));
} }
if (op->recv_trailing_metadata != NULL) { if (op->recv_trailing_metadata) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA")); gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA"));
} }
if (op->cancel_error != GRPC_ERROR_NONE) { if (op->cancel_stream) {
gpr_strvec_add(&b, gpr_strdup(" ")); gpr_strvec_add(&b, gpr_strdup(" "));
const char *msg = grpc_error_string(op->cancel_error); const char *msg =
grpc_error_string(op->payload->cancel_stream.cancel_error);
gpr_asprintf(&tmp, "CANCEL:%s", msg); gpr_asprintf(&tmp, "CANCEL:%s", msg);
gpr_strvec_add(&b, tmp); gpr_strvec_add(&b, tmp);
@ -204,8 +209,9 @@ char *grpc_transport_op_string(grpc_transport_op *op) {
} }
void grpc_call_log_op(char *file, int line, gpr_log_severity severity, void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
grpc_call_element *elem, grpc_transport_stream_op *op) { grpc_call_element *elem,
char *str = grpc_transport_stream_op_string(op); grpc_transport_stream_op_batch *op) {
char *str = grpc_transport_stream_op_batch_string(op);
gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str);
gpr_free(str); gpr_free(str);
} }

@ -69,9 +69,9 @@ void ChannelData::GetInfo(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
// CallData // CallData
void CallData::StartTransportStreamOp(grpc_exec_ctx *exec_ctx, void CallData::StartTransportStreamOpBatch(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
TransportStreamOp *op) { TransportStreamOpBatch *op) {
grpc_call_next_op(exec_ctx, elem, op->op()); grpc_call_next_op(exec_ctx, elem, op->op());
} }

@ -140,63 +140,80 @@ class TransportOp {
grpc_transport_op *op_; // Not owned. grpc_transport_op *op_; // Not owned.
}; };
/// A C++ wrapper for the \c grpc_transport_stream_op struct. /// A C++ wrapper for the \c grpc_transport_stream_op_batch struct.
class TransportStreamOp { class TransportStreamOpBatch {
public: public:
/// Borrows a pointer to \a op, but does NOT take ownership. /// Borrows a pointer to \a op, but does NOT take ownership.
/// The caller must ensure that \a op continues to exist for as /// The caller must ensure that \a op continues to exist for as
/// long as the TransportStreamOp object does. /// long as the TransportStreamOpBatch object does.
explicit TransportStreamOp(grpc_transport_stream_op *op) explicit TransportStreamOpBatch(grpc_transport_stream_op_batch *op)
: op_(op), : op_(op),
send_initial_metadata_(op->send_initial_metadata), send_initial_metadata_(
send_trailing_metadata_(op->send_trailing_metadata), op->send_initial_metadata
recv_initial_metadata_(op->recv_initial_metadata), ? op->payload->send_initial_metadata.send_initial_metadata
recv_trailing_metadata_(op->recv_trailing_metadata) {} : nullptr),
send_trailing_metadata_(
grpc_transport_stream_op *op() const { return op_; } op->send_trailing_metadata
? op->payload->send_trailing_metadata.send_trailing_metadata
: nullptr),
recv_initial_metadata_(
op->recv_initial_metadata
? op->payload->recv_initial_metadata.recv_initial_metadata
: nullptr),
recv_trailing_metadata_(
op->recv_trailing_metadata
? op->payload->recv_trailing_metadata.recv_trailing_metadata
: nullptr) {}
grpc_transport_stream_op_batch *op() const { return op_; }
grpc_closure *on_complete() const { return op_->on_complete; } grpc_closure *on_complete() const { return op_->on_complete; }
void set_on_complete(grpc_closure *closure) { op_->on_complete = closure; } void set_on_complete(grpc_closure *closure) { op_->on_complete = closure; }
MetadataBatch *send_initial_metadata() { MetadataBatch *send_initial_metadata() {
return op_->send_initial_metadata == nullptr ? nullptr return op_->send_initial_metadata ? &send_initial_metadata_ : nullptr;
: &send_initial_metadata_;
} }
MetadataBatch *send_trailing_metadata() { MetadataBatch *send_trailing_metadata() {
return op_->send_trailing_metadata == nullptr ? nullptr return op_->send_trailing_metadata ? &send_trailing_metadata_ : nullptr;
: &send_trailing_metadata_;
} }
MetadataBatch *recv_initial_metadata() { MetadataBatch *recv_initial_metadata() {
return op_->recv_initial_metadata == nullptr ? nullptr return op_->recv_initial_metadata ? &recv_initial_metadata_ : nullptr;
: &recv_initial_metadata_;
} }
MetadataBatch *recv_trailing_metadata() { MetadataBatch *recv_trailing_metadata() {
return op_->recv_trailing_metadata == nullptr ? nullptr return op_->recv_trailing_metadata ? &recv_trailing_metadata_ : nullptr;
: &recv_trailing_metadata_;
} }
uint32_t *send_initial_metadata_flags() const { uint32_t *send_initial_metadata_flags() const {
return &op_->send_initial_metadata_flags; return op_->send_initial_metadata
? &op_->payload->send_initial_metadata
.send_initial_metadata_flags
: nullptr;
} }
grpc_closure *recv_initial_metadata_ready() const { grpc_closure *recv_initial_metadata_ready() const {
return op_->recv_initial_metadata_ready; return op_->recv_initial_metadata
? op_->payload->recv_initial_metadata.recv_initial_metadata_ready
: nullptr;
} }
void set_recv_initial_metadata_ready(grpc_closure *closure) { void set_recv_initial_metadata_ready(grpc_closure *closure) {
op_->recv_initial_metadata_ready = closure; op_->payload->recv_initial_metadata.recv_initial_metadata_ready = closure;
} }
grpc_byte_stream *send_message() const { return op_->send_message; } grpc_byte_stream *send_message() const {
return op_->send_message ? op_->payload->send_message.send_message
: nullptr;
}
void set_send_message(grpc_byte_stream *send_message) { void set_send_message(grpc_byte_stream *send_message) {
op_->send_message = send_message; op_->send_message = true;
op_->payload->send_message.send_message = send_message;
} }
census_context *get_census_context() const { census_context *get_census_context() const {
return (census_context *)op_->context[GRPC_CONTEXT_TRACING].value; return (census_context *)op_->payload->context[GRPC_CONTEXT_TRACING].value;
} }
private: private:
grpc_transport_stream_op *op_; // Not owned. grpc_transport_stream_op_batch *op_; // Not owned.
MetadataBatch send_initial_metadata_; MetadataBatch send_initial_metadata_;
MetadataBatch send_trailing_metadata_; MetadataBatch send_trailing_metadata_;
MetadataBatch recv_initial_metadata_; MetadataBatch recv_initial_metadata_;
@ -240,9 +257,9 @@ class CallData {
// TODO(roth): Find a way to avoid passing elem into these methods. // TODO(roth): Find a way to avoid passing elem into these methods.
/// Starts a new stream operation. /// Starts a new stream operation.
virtual void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, virtual void StartTransportStreamOpBatch(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
TransportStreamOp *op); TransportStreamOpBatch *op);
/// Sets a pollset or pollset set. /// Sets a pollset or pollset set.
virtual void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx, virtual void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
@ -312,12 +329,12 @@ class ChannelFilter final {
reinterpret_cast<CallDataType *>(elem->call_data)->~CallDataType(); reinterpret_cast<CallDataType *>(elem->call_data)->~CallDataType();
} }
static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, static void StartTransportStreamOpBatch(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
CallDataType *call_data = (CallDataType *)elem->call_data; CallDataType *call_data = (CallDataType *)elem->call_data;
TransportStreamOp op_wrapper(op); TransportStreamOpBatch op_wrapper(op);
call_data->StartTransportStreamOp(exec_ctx, elem, &op_wrapper); call_data->StartTransportStreamOpBatch(exec_ctx, elem, &op_wrapper);
} }
static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx, static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
@ -369,7 +386,7 @@ void RegisterChannelFilter(
stack_type, stack_type,
priority, priority,
include_filter, include_filter,
{FilterType::StartTransportStreamOp, FilterType::StartTransportOp, {FilterType::StartTransportStreamOpBatch, FilterType::StartTransportOp,
FilterType::call_data_size, FilterType::InitCallElement, FilterType::call_data_size, FilterType::InitCallElement,
FilterType::SetPollsetOrPollsetSet, FilterType::DestroyCallElement, FilterType::SetPollsetOrPollsetSet, FilterType::DestroyCallElement,
FilterType::channel_data_size, FilterType::InitChannelElement, FilterType::channel_data_size, FilterType::InitChannelElement,

@ -113,6 +113,9 @@ message ClientConfig {
string other_client_api = 15; string other_client_api = 15;
repeated ChannelArg channel_args = 16; repeated ChannelArg channel_args = 16;
// Number of messages on a stream before it gets finished/restarted
int32 messages_per_stream = 18;
} }
message ClientStatus { ClientStats stats = 1; } message ClientStatus { ClientStats stats = 1; }

@ -73,7 +73,7 @@ static void call_destroy_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
} }
static void call_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, static void call_func(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
++*(int *)(elem->call_data); ++*(int *)(elem->call_data);
} }

@ -216,13 +216,14 @@ static void recv_im_ready(grpc_exec_ctx *exec_ctx, void *arg,
GRPC_STATUS_PERMISSION_DENIED)); GRPC_STATUS_PERMISSION_DENIED));
} }
static void start_transport_stream_op(grpc_exec_ctx *exec_ctx, static void start_transport_stream_op_batch(
grpc_call_element *elem, grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (op->recv_initial_metadata != NULL) { if (op->recv_initial_metadata) {
calld->recv_im_ready = op->recv_initial_metadata_ready; calld->recv_im_ready =
op->recv_initial_metadata_ready = op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
grpc_closure_create(recv_im_ready, elem, grpc_schedule_on_exec_ctx); grpc_closure_create(recv_im_ready, elem, grpc_schedule_on_exec_ctx);
} }
grpc_call_next_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op);
@ -248,7 +249,7 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem) {} grpc_channel_element *elem) {}
static const grpc_channel_filter test_filter = { static const grpc_channel_filter test_filter = {
start_transport_stream_op, start_transport_stream_op_batch,
grpc_channel_next_op, grpc_channel_next_op,
sizeof(call_data), sizeof(call_data),
init_call_elem, init_call_elem,

@ -57,7 +57,7 @@
should be shorter than CALL_DEADLINE_S - CQ_MAX_CONNECTION_AGE_WAIT_TIME_S */ should be shorter than CALL_DEADLINE_S - CQ_MAX_CONNECTION_AGE_WAIT_TIME_S */
#define CQ_MAX_CONNECTION_AGE_GRACE_WAIT_TIME_S 2 #define CQ_MAX_CONNECTION_AGE_GRACE_WAIT_TIME_S 2
/* The grace period for the test to observe the channel shutdown process */ /* The grace period for the test to observe the channel shutdown process */
#define IMMEDIATE_SHUTDOWN_GRACE_TIME_MS 300 #define IMMEDIATE_SHUTDOWN_GRACE_TIME_MS 3000
static void *tag(intptr_t t) { return (void *)t; } static void *tag(intptr_t t) { return (void *)t; }

@ -89,8 +89,8 @@ static void test_max_connection_idle(grpc_end2end_test_config config) {
/* wait for the channel to reach its maximum idle time */ /* wait for the channel to reach its maximum idle time */
grpc_channel_watch_connectivity_state( grpc_channel_watch_connectivity_state(
f.client, GRPC_CHANNEL_READY, f.client, GRPC_CHANNEL_READY,
grpc_timeout_milliseconds_to_deadline(MAX_CONNECTION_IDLE_MS + 500), f.cq, grpc_timeout_milliseconds_to_deadline(MAX_CONNECTION_IDLE_MS + 3000),
tag(99)); f.cq, tag(99));
CQ_EXPECT_COMPLETION(cqv, tag(99), 1); CQ_EXPECT_COMPLETION(cqv, tag(99), 1);
cq_verify(cqv); cq_verify(cqv);
state = grpc_channel_check_connectivity_state(f.client, 0); state = grpc_channel_check_connectivity_state(f.client, 0);

@ -47,13 +47,7 @@
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
static gpr_timespec random_deadline(void) { static gpr_atm random_deadline(void) { return rand(); }
gpr_timespec ts;
ts.tv_sec = rand();
ts.tv_nsec = rand();
ts.clock_type = GPR_CLOCK_REALTIME;
return ts;
}
static grpc_timer *create_test_elements(size_t num_elements) { static grpc_timer *create_test_elements(size_t num_elements) {
grpc_timer *elems = gpr_malloc(num_elements * sizeof(grpc_timer)); grpc_timer *elems = gpr_malloc(num_elements * sizeof(grpc_timer));
@ -78,12 +72,10 @@ static void check_valid(grpc_timer_heap *pq) {
size_t left_child = 1u + 2u * i; size_t left_child = 1u + 2u * i;
size_t right_child = left_child + 1u; size_t right_child = left_child + 1u;
if (left_child < pq->timer_count) { if (left_child < pq->timer_count) {
GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline, GPR_ASSERT(pq->timers[i]->deadline <= pq->timers[left_child]->deadline);
pq->timers[left_child]->deadline) <= 0);
} }
if (right_child < pq->timer_count) { if (right_child < pq->timer_count) {
GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline, GPR_ASSERT(pq->timers[i]->deadline <= pq->timers[right_child]->deadline);
pq->timers[right_child]->deadline) <= 0);
} }
} }
} }
@ -227,20 +219,19 @@ static void test2(void) {
} }
if (num_inserted) { if (num_inserted) {
gpr_timespec *min_deadline = NULL; gpr_atm *min_deadline = NULL;
for (size_t i = 0; i < elems_size; i++) { for (size_t i = 0; i < elems_size; i++) {
if (elems[i].inserted) { if (elems[i].inserted) {
if (min_deadline == NULL) { if (min_deadline == NULL) {
min_deadline = &elems[i].elem.deadline; min_deadline = &elems[i].elem.deadline;
} else { } else {
if (gpr_time_cmp(elems[i].elem.deadline, *min_deadline) < 0) { if (elems[i].elem.deadline < *min_deadline) {
min_deadline = &elems[i].elem.deadline; min_deadline = &elems[i].elem.deadline;
} }
} }
} }
} }
GPR_ASSERT( GPR_ASSERT(grpc_timer_heap_top(&pq)->deadline == *min_deadline);
0 == gpr_time_cmp(grpc_timer_heap_top(&pq)->deadline, *min_deadline));
} }
} }

@ -45,6 +45,9 @@
#define MAX_CB 30 #define MAX_CB 30
extern int grpc_timer_trace;
extern int grpc_timer_check_trace;
static int cb_called[MAX_CB][2]; static int cb_called[MAX_CB][2];
static void cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { static void cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
@ -57,7 +60,11 @@ static void add_test(void) {
grpc_timer timers[20]; grpc_timer timers[20];
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
gpr_log(GPR_INFO, "add_test");
grpc_timer_list_init(start); grpc_timer_list_init(start);
grpc_timer_trace = 1;
grpc_timer_check_trace = 1;
memset(cb_called, 0, sizeof(cb_called)); memset(cb_called, 0, sizeof(cb_called));
/* 10 ms timers. will expire in the current epoch */ /* 10 ms timers. will expire in the current epoch */
@ -120,9 +127,7 @@ static void add_test(void) {
} }
static gpr_timespec tfm(int m) { static gpr_timespec tfm(int m) {
gpr_timespec t = gpr_time_from_millis(m, GPR_TIMESPAN); return gpr_time_from_millis(m, GPR_CLOCK_REALTIME);
t.clock_type = GPR_CLOCK_REALTIME;
return t;
} }
/* Cleaning up a list with pending timers. */ /* Cleaning up a list with pending timers. */
@ -130,7 +135,11 @@ void destruction_test(void) {
grpc_timer timers[5]; grpc_timer timers[5];
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
gpr_log(GPR_INFO, "destruction_test");
grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME)); grpc_timer_list_init(gpr_time_0(GPR_CLOCK_REALTIME));
grpc_timer_trace = 1;
grpc_timer_check_trace = 1;
memset(cb_called, 0, sizeof(cb_called)); memset(cb_called, 0, sizeof(cb_called));
grpc_timer_init( grpc_timer_init(
@ -170,6 +179,7 @@ void destruction_test(void) {
int main(int argc, char **argv) { int main(int argc, char **argv) {
grpc_test_init(argc, argv); grpc_test_init(argc, argv);
gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
add_test(); add_test();
destruction_test(); destruction_test();
return 0; return 0;

@ -83,9 +83,13 @@ static void test(const char *name, size_t init_size, const size_t *allocs,
static const size_t allocs_##name[] = {__VA_ARGS__}; \ static const size_t allocs_##name[] = {__VA_ARGS__}; \
test(#name, init_size, allocs_##name, GPR_ARRAY_SIZE(allocs_##name)) test(#name, init_size, allocs_##name, GPR_ARRAY_SIZE(allocs_##name))
#define CONCURRENT_TEST_ITERATIONS 100000
#define CONCURRENT_TEST_THREADS 100 #define CONCURRENT_TEST_THREADS 100
size_t concurrent_test_iterations() {
if (sizeof(void *) < 8) return 1000;
return 100000;
}
typedef struct { typedef struct {
gpr_event ev_start; gpr_event ev_start;
gpr_arena *arena; gpr_arena *arena;
@ -94,7 +98,7 @@ typedef struct {
static void concurrent_test_body(void *arg) { static void concurrent_test_body(void *arg) {
concurrent_test_args *a = arg; concurrent_test_args *a = arg;
gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME)); gpr_event_wait(&a->ev_start, gpr_inf_future(GPR_CLOCK_REALTIME));
for (size_t i = 0; i < CONCURRENT_TEST_ITERATIONS; i++) { for (size_t i = 0; i < concurrent_test_iterations(); i++) {
*(char *)gpr_arena_alloc(a->arena, 1) = (char)i; *(char *)gpr_arena_alloc(a->arena, 1) = (char)i;
} }
} }

@ -122,8 +122,9 @@ class ChannelDataImpl : public ChannelData {
class CallDataImpl : public CallData { class CallDataImpl : public CallData {
public: public:
void StartTransportStreamOp(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, void StartTransportStreamOpBatch(grpc_exec_ctx* exec_ctx,
TransportStreamOp* op) override { grpc_call_element* elem,
TransportStreamOpBatch* op) override {
// Incrementing the counter could be done from Init(), but we want // Incrementing the counter could be done from Init(), but we want
// to test that the individual methods are actually called correctly. // to test that the individual methods are actually called correctly.
if (op->recv_initial_metadata() != nullptr) IncrementCallCounter(); if (op->recv_initial_metadata() != nullptr) IncrementCallCounter();

@ -221,7 +221,7 @@ namespace dummy_filter {
static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) {} grpc_transport_stream_op_batch *op) {}
static void StartTransportOp(grpc_exec_ctx *exec_ctx, static void StartTransportOp(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem, grpc_channel_element *elem,
@ -296,7 +296,7 @@ void SetPollsetSet(grpc_exec_ctx *exec_ctx, grpc_transport *self,
/* implementation of grpc_transport_perform_stream_op */ /* implementation of grpc_transport_perform_stream_op */
void PerformStreamOp(grpc_exec_ctx *exec_ctx, grpc_transport *self, void PerformStreamOp(grpc_exec_ctx *exec_ctx, grpc_transport *self,
grpc_stream *stream, grpc_transport_stream_op *op) { grpc_stream *stream, grpc_transport_stream_op_batch *op) {
grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE); grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE);
} }
@ -346,13 +346,15 @@ class SendEmptyMetadata {
memset(&op_, 0, sizeof(op_)); memset(&op_, 0, sizeof(op_));
op_.on_complete = grpc_closure_init(&closure_, DoNothing, nullptr, op_.on_complete = grpc_closure_init(&closure_, DoNothing, nullptr,
grpc_schedule_on_exec_ctx); grpc_schedule_on_exec_ctx);
op_.send_initial_metadata = true;
op_.payload = &op_payload_;
} }
class Op { class Op {
public: public:
Op(grpc_exec_ctx *exec_ctx, SendEmptyMetadata *p, grpc_call_stack *s) { Op(grpc_exec_ctx *exec_ctx, SendEmptyMetadata *p, grpc_call_stack *s) {
grpc_metadata_batch_init(&batch_); grpc_metadata_batch_init(&batch_);
p->op_.send_initial_metadata = &batch_; p->op_payload_.send_initial_metadata.send_initial_metadata = &batch_;
} }
void Finish(grpc_exec_ctx *exec_ctx) { void Finish(grpc_exec_ctx *exec_ctx) {
grpc_metadata_batch_destroy(exec_ctx, &batch_); grpc_metadata_batch_destroy(exec_ctx, &batch_);
@ -366,7 +368,8 @@ class SendEmptyMetadata {
const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC); const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
const gpr_timespec start_time_ = gpr_now(GPR_CLOCK_MONOTONIC); const gpr_timespec start_time_ = gpr_now(GPR_CLOCK_MONOTONIC);
const grpc_slice method_ = grpc_slice_from_static_string("/foo/bar"); const grpc_slice method_ = grpc_slice_from_static_string("/foo/bar");
grpc_transport_stream_op op_; grpc_transport_stream_op_batch op_;
grpc_transport_stream_op_batch_payload op_payload_;
grpc_closure closure_; grpc_closure closure_;
}; };
@ -488,13 +491,16 @@ namespace isolated_call_filter {
static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx, static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem, grpc_call_element *elem,
grpc_transport_stream_op *op) { grpc_transport_stream_op_batch *op) {
if (op->recv_initial_metadata) { if (op->recv_initial_metadata) {
grpc_closure_sched(exec_ctx, op->recv_initial_metadata_ready, grpc_closure_sched(
GRPC_ERROR_NONE); exec_ctx,
op->payload->recv_initial_metadata.recv_initial_metadata_ready,
GRPC_ERROR_NONE);
} }
if (op->recv_message) { if (op->recv_message) {
grpc_closure_sched(exec_ctx, op->recv_message_ready, GRPC_ERROR_NONE); grpc_closure_sched(exec_ctx, op->payload->recv_message.recv_message_ready,
GRPC_ERROR_NONE);
} }
grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE); grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE);
} }

@ -207,7 +207,7 @@ class Stream {
static_cast<grpc_stream *>(stream_), closure); static_cast<grpc_stream *>(stream_), closure);
} }
void Op(grpc_transport_stream_op *op) { void Op(grpc_transport_stream_op_batch *op) {
grpc_transport_perform_stream_op(f_->exec_ctx(), f_->transport(), grpc_transport_perform_stream_op(f_->exec_ctx(), f_->transport(),
static_cast<grpc_stream *>(stream_), op); static_cast<grpc_stream *>(stream_), op);
} }
@ -305,10 +305,16 @@ static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State &state) {
TrackCounters track_counters; TrackCounters track_counters;
Fixture f(grpc::ChannelArguments(), true); Fixture f(grpc::ChannelArguments(), true);
Stream s(&f); Stream s(&f);
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
grpc_transport_stream_op_batch_payload op_payload;
std::unique_ptr<Closure> start; std::unique_ptr<Closure> start;
std::unique_ptr<Closure> done; std::unique_ptr<Closure> done;
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
};
grpc_metadata_batch b; grpc_metadata_batch b;
grpc_metadata_batch_init(&b); grpc_metadata_batch_init(&b);
b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); b.deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
@ -324,14 +330,16 @@ static void BM_StreamCreateSendInitialMetadataDestroy(benchmark::State &state) {
start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { start = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return; if (!state.KeepRunning()) return;
s.Init(state); s.Init(state);
memset(&op, 0, sizeof(op)); reset_op();
op.on_complete = done.get(); op.on_complete = done.get();
op.send_initial_metadata = &b; op.send_initial_metadata = true;
op.payload->send_initial_metadata.send_initial_metadata = &b;
s.Op(&op); s.Op(&op);
}); });
done = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { done = MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
memset(&op, 0, sizeof(op)); reset_op();
op.cancel_error = GRPC_ERROR_CANCELLED; op.cancel_stream = true;
op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op); s.Op(&op);
s.DestroyThen(start.get()); s.DestroyThen(start.get());
}); });
@ -348,11 +356,16 @@ static void BM_TransportEmptyOp(benchmark::State &state) {
Fixture f(grpc::ChannelArguments(), true); Fixture f(grpc::ChannelArguments(), true);
Stream s(&f); Stream s(&f);
s.Init(state); s.Init(state);
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
grpc_transport_stream_op_batch_payload op_payload;
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
};
std::unique_ptr<Closure> c = std::unique_ptr<Closure> c =
MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) { MakeClosure([&](grpc_exec_ctx *exec_ctx, grpc_error *error) {
if (!state.KeepRunning()) return; if (!state.KeepRunning()) return;
memset(&op, 0, sizeof(op)); reset_op();
op.on_complete = c.get(); op.on_complete = c.get();
s.Op(&op); s.Op(&op);
}); });
@ -370,7 +383,12 @@ static void BM_TransportStreamSend(benchmark::State &state) {
Fixture f(grpc::ChannelArguments(), true); Fixture f(grpc::ChannelArguments(), true);
Stream s(&f); Stream s(&f);
s.Init(state); s.Init(state);
grpc_transport_stream_op op; grpc_transport_stream_op_batch op;
grpc_transport_stream_op_batch_payload op_payload;
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
};
grpc_slice_buffer_stream send_stream; grpc_slice_buffer_stream send_stream;
grpc_slice_buffer send_buffer; grpc_slice_buffer send_buffer;
grpc_slice_buffer_init(&send_buffer); grpc_slice_buffer_init(&send_buffer);
@ -397,20 +415,23 @@ static void BM_TransportStreamSend(benchmark::State &state) {
s.chttp2_stream()->outgoing_window_delta = 1024 * 1024 * 1024; s.chttp2_stream()->outgoing_window_delta = 1024 * 1024 * 1024;
f.chttp2_transport()->outgoing_window = 1024 * 1024 * 1024; f.chttp2_transport()->outgoing_window = 1024 * 1024 * 1024;
grpc_slice_buffer_stream_init(&send_stream, &send_buffer, 0); grpc_slice_buffer_stream_init(&send_stream, &send_buffer, 0);
memset(&op, 0, sizeof(op)); reset_op();
op.on_complete = c.get(); op.on_complete = c.get();
op.send_message = &send_stream.base; op.send_message = true;
op.payload->send_message.send_message = &send_stream.base;
s.Op(&op); s.Op(&op);
}); });
memset(&op, 0, sizeof(op)); reset_op();
op.send_initial_metadata = &b; op.send_initial_metadata = true;
op.payload->send_initial_metadata.send_initial_metadata = &b;
op.on_complete = c.get(); op.on_complete = c.get();
s.Op(&op); s.Op(&op);
f.FlushExecCtx(); f.FlushExecCtx();
memset(&op, 0, sizeof(op)); reset_op();
op.cancel_error = GRPC_ERROR_CANCELLED; op.cancel_stream = true;
op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op); s.Op(&op);
s.DestroyThen( s.DestroyThen(
MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {})); MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}));
@ -483,10 +504,16 @@ static void BM_TransportStreamRecv(benchmark::State &state) {
Fixture f(grpc::ChannelArguments(), true); Fixture f(grpc::ChannelArguments(), true);
Stream s(&f); Stream s(&f);
s.Init(state); s.Init(state);
grpc_transport_stream_op op; grpc_transport_stream_op_batch_payload op_payload;
grpc_transport_stream_op_batch op;
grpc_byte_stream *recv_stream; grpc_byte_stream *recv_stream;
grpc_slice incoming_data = CreateIncomingDataSlice(state.range(0), 16384); grpc_slice incoming_data = CreateIncomingDataSlice(state.range(0), 16384);
auto reset_op = [&]() {
memset(&op, 0, sizeof(op));
op.payload = &op_payload;
};
grpc_metadata_batch b; grpc_metadata_batch b;
grpc_metadata_batch_init(&b); grpc_metadata_batch_init(&b);
grpc_metadata_batch b_recv; grpc_metadata_batch b_recv;
@ -518,10 +545,11 @@ static void BM_TransportStreamRecv(benchmark::State &state) {
s.chttp2_stream()->incoming_window_delta = 1024 * 1024 * 1024; s.chttp2_stream()->incoming_window_delta = 1024 * 1024 * 1024;
f.chttp2_transport()->incoming_window = 1024 * 1024 * 1024; f.chttp2_transport()->incoming_window = 1024 * 1024 * 1024;
received = 0; received = 0;
memset(&op, 0, sizeof(op)); reset_op();
op.on_complete = do_nothing.get(); op.on_complete = do_nothing.get();
op.recv_message = &recv_stream; op.recv_message = true;
op.recv_message_ready = drain_start.get(); op.payload->recv_message.recv_message = &recv_stream;
op.payload->recv_message.recv_message_ready = drain_start.get();
s.Op(&op); s.Op(&op);
f.PushInput(grpc_slice_ref(incoming_data)); f.PushInput(grpc_slice_ref(incoming_data));
}); });
@ -552,9 +580,13 @@ static void BM_TransportStreamRecv(benchmark::State &state) {
grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE); grpc_closure_run(exec_ctx, drain.get(), GRPC_ERROR_NONE);
}); });
memset(&op, 0, sizeof(op)); reset_op();
op.send_initial_metadata = &b; op.send_initial_metadata = true;
op.recv_initial_metadata = &b_recv; op.payload->send_initial_metadata.send_initial_metadata = &b;
op.recv_initial_metadata = true;
op.payload->recv_initial_metadata.recv_initial_metadata = &b_recv;
op.payload->recv_initial_metadata.recv_initial_metadata_ready =
do_nothing.get();
op.on_complete = c.get(); op.on_complete = c.get();
s.Op(&op); s.Op(&op);
f.PushInput(SLICE_FROM_BUFFER( f.PushInput(SLICE_FROM_BUFFER(
@ -571,8 +603,9 @@ static void BM_TransportStreamRecv(benchmark::State &state) {
"\x10\x14grpc-accept-encoding\x15identity,deflate,gzip")); "\x10\x14grpc-accept-encoding\x15identity,deflate,gzip"));
f.FlushExecCtx(); f.FlushExecCtx();
memset(&op, 0, sizeof(op)); reset_op();
op.cancel_error = GRPC_ERROR_CANCELLED; op.cancel_stream = true;
op.payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED;
s.Op(&op); s.Op(&op);
s.DestroyThen( s.DestroyThen(
MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {})); MakeOnceClosure([](grpc_exec_ctx *exec_ctx, grpc_error *error) {}));

@ -63,13 +63,13 @@ class ClientRpcContext {
virtual ~ClientRpcContext() {} virtual ~ClientRpcContext() {}
// next state, return false if done. Collect stats when appropriate // next state, return false if done. Collect stats when appropriate
virtual bool RunNextState(bool, HistogramEntry* entry) = 0; virtual bool RunNextState(bool, HistogramEntry* entry) = 0;
virtual ClientRpcContext* StartNewClone() = 0; virtual void StartNewClone(CompletionQueue* cq) = 0;
static void* tag(ClientRpcContext* c) { return reinterpret_cast<void*>(c); } static void* tag(ClientRpcContext* c) { return reinterpret_cast<void*>(c); }
static ClientRpcContext* detag(void* t) { static ClientRpcContext* detag(void* t) {
return reinterpret_cast<ClientRpcContext*>(t); return reinterpret_cast<ClientRpcContext*>(t);
} }
virtual void Start(CompletionQueue* cq) = 0; virtual void Start(CompletionQueue* cq, const ClientConfig& config) = 0;
}; };
template <class RequestType, class ResponseType> template <class RequestType, class ResponseType>
@ -94,22 +94,17 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
next_issue_(next_issue), next_issue_(next_issue),
start_req_(start_req) {} start_req_(start_req) {}
~ClientRpcContextUnaryImpl() override {} ~ClientRpcContextUnaryImpl() override {}
void Start(CompletionQueue* cq) override { void Start(CompletionQueue* cq, const ClientConfig& config) override {
cq_ = cq; StartInternal(cq);
if (!next_issue_) { // ready to issue
RunNextState(true, nullptr);
} else { // wait for the issue time
alarm_.reset(new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
}
} }
bool RunNextState(bool ok, HistogramEntry* entry) override { bool RunNextState(bool ok, HistogramEntry* entry) override {
switch (next_state_) { switch (next_state_) {
case State::READY: case State::READY:
start_ = UsageTimer::Now(); start_ = UsageTimer::Now();
response_reader_ = start_req_(stub_, &context_, req_, cq_); response_reader_ = start_req_(stub_, &context_, req_, cq_);
next_state_ = State::RESP_DONE;
response_reader_->Finish(&response_, &status_, response_reader_->Finish(&response_, &status_,
ClientRpcContext::tag(this)); ClientRpcContext::tag(this));
next_state_ = State::RESP_DONE;
return true; return true;
case State::RESP_DONE: case State::RESP_DONE:
if (status_.ok()) { if (status_.ok()) {
@ -123,9 +118,10 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
return false; return false;
} }
} }
ClientRpcContext* StartNewClone() override { void StartNewClone(CompletionQueue* cq) override {
return new ClientRpcContextUnaryImpl(stub_, req_, next_issue_, start_req_, auto* clone = new ClientRpcContextUnaryImpl(stub_, req_, next_issue_,
callback_); start_req_, callback_);
clone->StartInternal(cq);
} }
private: private:
@ -147,6 +143,15 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
double start_; double start_;
std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>> std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>
response_reader_; response_reader_;
void StartInternal(CompletionQueue* cq) {
cq_ = cq;
if (!next_issue_) { // ready to issue
RunNextState(true, nullptr);
} else { // wait for the issue time
alarm_.reset(new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
}
}
}; };
typedef std::forward_list<ClientRpcContext*> context_list; typedef std::forward_list<ClientRpcContext*> context_list;
@ -185,7 +190,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
auto* cq = cli_cqs_[t].get(); auto* cq = cli_cqs_[t].get();
auto ctx = auto ctx =
setup_ctx(channels_[ch].get_stub(), next_issuers_[t], request_); setup_ctx(channels_[ch].get_stub(), next_issuers_[t], request_);
ctx->Start(cq); ctx->Start(cq, config);
} }
t = (t + 1) % cli_cqs_.size(); t = (t + 1) % cli_cqs_.size();
} }
@ -248,8 +253,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
} else if (!ctx->RunNextState(ok, entry)) { } else if (!ctx->RunNextState(ok, entry)) {
// The RPC and callback are done, so clone the ctx // The RPC and callback are done, so clone the ctx
// and kickstart the new one // and kickstart the new one
auto clone = ctx->StartNewClone(); ctx->StartNewClone(cli_cqs_[thread_idx].get());
clone->Start(cli_cqs_[thread_idx].get());
// delete the old version // delete the old version
delete ctx; delete ctx;
} }
@ -330,10 +334,8 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
next_issue_(next_issue), next_issue_(next_issue),
start_req_(start_req) {} start_req_(start_req) {}
~ClientRpcContextStreamingImpl() override {} ~ClientRpcContextStreamingImpl() override {}
void Start(CompletionQueue* cq) override { void Start(CompletionQueue* cq, const ClientConfig& config) override {
cq_ = cq; StartInternal(cq, config.messages_per_stream());
stream_ = start_req_(stub_, &context_, cq, ClientRpcContext::tag(this));
next_state_ = State::STREAM_IDLE;
} }
bool RunNextState(bool ok, HistogramEntry* entry) override { bool RunNextState(bool ok, HistogramEntry* entry) override {
while (true) { while (true) {
@ -346,9 +348,9 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
} }
break; // loop around, don't return break; // loop around, don't return
case State::WAIT: case State::WAIT:
next_state_ = State::READY_TO_WRITE;
alarm_.reset( alarm_.reset(
new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this))); new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
next_state_ = State::READY_TO_WRITE;
return true; return true;
case State::READY_TO_WRITE: case State::READY_TO_WRITE:
if (!ok) { if (!ok) {
@ -369,17 +371,32 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
case State::READ_DONE: case State::READ_DONE:
entry->set_value((UsageTimer::Now() - start_) * 1e9); entry->set_value((UsageTimer::Now() - start_) * 1e9);
callback_(status_, &response_); callback_(status_, &response_);
if ((messages_per_stream_ != 0) &&
(++messages_issued_ >= messages_per_stream_)) {
next_state_ = State::WRITES_DONE_DONE;
stream_->WritesDone(ClientRpcContext::tag(this));
return true;
}
next_state_ = State::STREAM_IDLE; next_state_ = State::STREAM_IDLE;
break; // loop around break; // loop around
case State::WRITES_DONE_DONE:
next_state_ = State::FINISH_DONE;
stream_->Finish(&status_, ClientRpcContext::tag(this));
return true;
case State::FINISH_DONE:
next_state_ = State::INVALID;
return false;
break;
default: default:
GPR_ASSERT(false); GPR_ASSERT(false);
return false; return false;
} }
} }
} }
ClientRpcContext* StartNewClone() override { void StartNewClone(CompletionQueue* cq) override {
return new ClientRpcContextStreamingImpl(stub_, req_, next_issue_, auto* clone = new ClientRpcContextStreamingImpl(stub_, req_, next_issue_,
start_req_, callback_); start_req_, callback_);
clone->StartInternal(cq, messages_per_stream_);
} }
private: private:
@ -395,7 +412,9 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
WAIT, WAIT,
READY_TO_WRITE, READY_TO_WRITE,
WRITE_DONE, WRITE_DONE,
READ_DONE READ_DONE,
WRITES_DONE_DONE,
FINISH_DONE
}; };
State next_state_; State next_state_;
std::function<void(grpc::Status, ResponseType*)> callback_; std::function<void(grpc::Status, ResponseType*)> callback_;
@ -408,6 +427,18 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
double start_; double start_;
std::unique_ptr<grpc::ClientAsyncReaderWriter<RequestType, ResponseType>> std::unique_ptr<grpc::ClientAsyncReaderWriter<RequestType, ResponseType>>
stream_; stream_;
// Allow a limit on number of messages in a stream
int messages_per_stream_;
int messages_issued_;
void StartInternal(CompletionQueue* cq, int messages_per_stream) {
cq_ = cq;
next_state_ = State::STREAM_IDLE;
stream_ = start_req_(stub_, &context_, cq, ClientRpcContext::tag(this));
messages_per_stream_ = messages_per_stream;
messages_issued_ = 0;
}
}; };
class AsyncStreamingClient final class AsyncStreamingClient final
@ -459,13 +490,8 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
next_issue_(next_issue), next_issue_(next_issue),
start_req_(start_req) {} start_req_(start_req) {}
~ClientRpcContextGenericStreamingImpl() override {} ~ClientRpcContextGenericStreamingImpl() override {}
void Start(CompletionQueue* cq) override { void Start(CompletionQueue* cq, const ClientConfig& config) override {
cq_ = cq; StartInternal(cq, config.messages_per_stream());
const grpc::string kMethodName(
"/grpc.testing.BenchmarkService/StreamingCall");
stream_ = start_req_(stub_, &context_, kMethodName, cq,
ClientRpcContext::tag(this));
next_state_ = State::STREAM_IDLE;
} }
bool RunNextState(bool ok, HistogramEntry* entry) override { bool RunNextState(bool ok, HistogramEntry* entry) override {
while (true) { while (true) {
@ -478,9 +504,9 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
} }
break; // loop around, don't return break; // loop around, don't return
case State::WAIT: case State::WAIT:
next_state_ = State::READY_TO_WRITE;
alarm_.reset( alarm_.reset(
new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this))); new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
next_state_ = State::READY_TO_WRITE;
return true; return true;
case State::READY_TO_WRITE: case State::READY_TO_WRITE:
if (!ok) { if (!ok) {
@ -501,17 +527,32 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
case State::READ_DONE: case State::READ_DONE:
entry->set_value((UsageTimer::Now() - start_) * 1e9); entry->set_value((UsageTimer::Now() - start_) * 1e9);
callback_(status_, &response_); callback_(status_, &response_);
if ((messages_per_stream_ != 0) &&
(++messages_issued_ >= messages_per_stream_)) {
next_state_ = State::WRITES_DONE_DONE;
stream_->WritesDone(ClientRpcContext::tag(this));
return true;
}
next_state_ = State::STREAM_IDLE; next_state_ = State::STREAM_IDLE;
break; // loop around break; // loop around
case State::WRITES_DONE_DONE:
next_state_ = State::FINISH_DONE;
stream_->Finish(&status_, ClientRpcContext::tag(this));
return true;
case State::FINISH_DONE:
next_state_ = State::INVALID;
return false;
break;
default: default:
GPR_ASSERT(false); GPR_ASSERT(false);
return false; return false;
} }
} }
} }
ClientRpcContext* StartNewClone() override { void StartNewClone(CompletionQueue* cq) override {
return new ClientRpcContextGenericStreamingImpl(stub_, req_, next_issue_, auto* clone = new ClientRpcContextGenericStreamingImpl(
start_req_, callback_); stub_, req_, next_issue_, start_req_, callback_);
clone->StartInternal(cq, messages_per_stream_);
} }
private: private:
@ -527,7 +568,9 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
WAIT, WAIT,
READY_TO_WRITE, READY_TO_WRITE,
WRITE_DONE, WRITE_DONE,
READ_DONE READ_DONE,
WRITES_DONE_DONE,
FINISH_DONE
}; };
State next_state_; State next_state_;
std::function<void(grpc::Status, ByteBuffer*)> callback_; std::function<void(grpc::Status, ByteBuffer*)> callback_;
@ -539,6 +582,21 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
grpc::Status status_; grpc::Status status_;
double start_; double start_;
std::unique_ptr<grpc::GenericClientAsyncReaderWriter> stream_; std::unique_ptr<grpc::GenericClientAsyncReaderWriter> stream_;
// Allow a limit on number of messages in a stream
int messages_per_stream_;
int messages_issued_;
void StartInternal(CompletionQueue* cq, int messages_per_stream) {
cq_ = cq;
const grpc::string kMethodName(
"/grpc.testing.BenchmarkService/StreamingCall");
next_state_ = State::STREAM_IDLE;
stream_ = start_req_(stub_, &context_, kMethodName, cq,
ClientRpcContext::tag(this));
messages_per_stream_ = messages_per_stream;
messages_issued_ = 0;
}
}; };
static std::unique_ptr<grpc::GenericStub> GenericStubCreator( static std::unique_ptr<grpc::GenericStub> GenericStubCreator(

@ -142,10 +142,13 @@ class SynchronousStreamingClient final : public SynchronousClient {
SynchronousStreamingClient(const ClientConfig& config) SynchronousStreamingClient(const ClientConfig& config)
: SynchronousClient(config), : SynchronousClient(config),
context_(num_threads_), context_(num_threads_),
stream_(num_threads_) { stream_(num_threads_),
messages_per_stream_(config.messages_per_stream()),
messages_issued_(num_threads_) {
for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) { for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
auto* stub = channels_[thread_idx % channels_.size()].get_stub(); auto* stub = channels_[thread_idx % channels_.size()].get_stub();
stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]); stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
messages_issued_[thread_idx] = 0;
} }
StartThreads(num_threads_); StartThreads(num_threads_);
} }
@ -173,11 +176,17 @@ class SynchronousStreamingClient final : public SynchronousClient {
stream_[thread_idx]->Read(&responses_[thread_idx])) { stream_[thread_idx]->Read(&responses_[thread_idx])) {
entry->set_value((UsageTimer::Now() - start) * 1e9); entry->set_value((UsageTimer::Now() - start) * 1e9);
// don't set the status since there isn't one yet // don't set the status since there isn't one yet
return true; if ((messages_per_stream_ != 0) &&
(++messages_issued_[thread_idx] < messages_per_stream_)) {
return true;
} else {
// Fall through to the below resetting code after finish
}
} }
stream_[thread_idx]->WritesDone(); stream_[thread_idx]->WritesDone();
Status s = stream_[thread_idx]->Finish(); Status s = stream_[thread_idx]->Finish();
// don't set the value since the stream is failed and shouldn't be timed // don't set the value since this is either a failure (shouldn't be timed)
// or a stream-end (already has been timed)
entry->set_status(s.error_code()); entry->set_status(s.error_code());
if (!s.ok()) { if (!s.ok()) {
gpr_log(GPR_ERROR, "Stream %" PRIuPTR " received an error %s", thread_idx, gpr_log(GPR_ERROR, "Stream %" PRIuPTR " received an error %s", thread_idx,
@ -187,6 +196,7 @@ class SynchronousStreamingClient final : public SynchronousClient {
context_[thread_idx].~ClientContext(); context_[thread_idx].~ClientContext();
new (&context_[thread_idx]) ClientContext(); new (&context_[thread_idx]) ClientContext();
stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]); stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
messages_issued_[thread_idx] = 0;
return true; return true;
} }
@ -197,6 +207,8 @@ class SynchronousStreamingClient final : public SynchronousClient {
std::vector< std::vector<
std::unique_ptr<grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>> std::unique_ptr<grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>>
stream_; stream_;
const int messages_per_stream_;
std::vector<int> messages_issued_;
}; };
std::unique_ptr<Client> CreateSynchronousUnaryClient( std::unique_ptr<Client> CreateSynchronousUnaryClient(

File diff suppressed because it is too large Load Diff

@ -112,6 +112,7 @@ def _ping_pong_scenario(name, rpc_type,
channels=None, channels=None,
outstanding=None, outstanding=None,
resource_quota_size=None, resource_quota_size=None,
messages_per_stream=None,
excluded_poll_engines=[]): excluded_poll_engines=[]):
"""Creates a basic ping pong scenario.""" """Creates a basic ping pong scenario."""
scenario = { scenario = {
@ -165,6 +166,8 @@ def _ping_pong_scenario(name, rpc_type,
scenario['client_config']['client_channels'] = 1 scenario['client_config']['client_channels'] = 1
scenario['client_config']['async_client_threads'] = 1 scenario['client_config']['async_client_threads'] = 1
if messages_per_stream:
scenario['client_config']['messages_per_stream'] = messages_per_stream
if client_language: if client_language:
# the CLIENT_LANGUAGE field is recognized by run_performance_tests.py # the CLIENT_LANGUAGE field is recognized by run_performance_tests.py
scenario['CLIENT_LANGUAGE'] = client_language scenario['CLIENT_LANGUAGE'] = client_language
@ -214,6 +217,26 @@ class CXXLanguage:
secure=secure, secure=secure,
categories=smoketest_categories+[SCALABLE]) categories=smoketest_categories+[SCALABLE])
for mps in geometric_progression(1, 20, 10):
yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_unconstrained_%smps_%s' % (mps, secstr),
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_GENERIC_SERVER',
unconstrained_client='async', use_generic_payload=True,
secure=secure, messages_per_stream=mps,
categories=smoketest_categories+[SCALABLE])
for mps in geometric_progression(1, 200, math.sqrt(10)):
yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_unconstrained_%smps_%s' % (mps, secstr),
rpc_type='STREAMING',
client_type='ASYNC_CLIENT',
server_type='ASYNC_GENERIC_SERVER',
unconstrained_client='async', use_generic_payload=True,
secure=secure, messages_per_stream=mps,
categories=[SWEEP])
yield _ping_pong_scenario( yield _ping_pong_scenario(
'cpp_generic_async_streaming_qps_1channel_1MBmsg_%s' % secstr, 'cpp_generic_async_streaming_qps_1channel_1MBmsg_%s' % secstr,
rpc_type='STREAMING', rpc_type='STREAMING',
@ -331,6 +354,27 @@ class CXXLanguage:
# categories=smoketest_categories+[SCALABLE], # categories=smoketest_categories+[SCALABLE],
# resource_quota_size=500*1024) # resource_quota_size=500*1024)
if rpc_type == 'streaming':
for mps in geometric_progression(1, 20, 10):
yield _ping_pong_scenario(
'cpp_protobuf_%s_%s_qps_unconstrained_%smps_%s' % (synchronicity, rpc_type, mps, secstr),
rpc_type=rpc_type.upper(),
client_type='%s_CLIENT' % synchronicity.upper(),
server_type='%s_SERVER' % synchronicity.upper(),
unconstrained_client=synchronicity,
secure=secure, messages_per_stream=mps,
categories=smoketest_categories+[SCALABLE])
for mps in geometric_progression(1, 200, math.sqrt(10)):
yield _ping_pong_scenario(
'cpp_protobuf_%s_%s_qps_unconstrained_%smps_%s' % (synchronicity, rpc_type, mps, secstr),
rpc_type=rpc_type.upper(),
client_type='%s_CLIENT' % synchronicity.upper(),
server_type='%s_SERVER' % synchronicity.upper(),
unconstrained_client=synchronicity,
secure=secure, messages_per_stream=mps,
categories=[SWEEP])
for channels in geometric_progression(1, 20000, math.sqrt(10)): for channels in geometric_progression(1, 20000, math.sqrt(10)):
for outstanding in geometric_progression(1, 200000, math.sqrt(10)): for outstanding in geometric_progression(1, 200000, math.sqrt(10)):
if synchronicity == 'sync' and outstanding > 1200: continue if synchronicity == 'sync' and outstanding > 1200: continue

Loading…
Cancel
Save