|  | @@ -84,8 +84,6 @@ grpc_tracer_flag grpc_trace_chttp2_refcount =
 | 
	
		
			
				|  |  |      GRPC_TRACER_INITIALIZER(false, "chttp2_refcount");
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static const grpc_transport_vtable vtable;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  /* forward declarations of various callbacks that we'll build closures around */
 | 
	
		
			
				|  |  |  static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *t,
 | 
	
		
			
				|  |  |                                        grpc_error *error);
 | 
	
	
		
			
				|  | @@ -248,6 +246,8 @@ void grpc_chttp2_unref_transport(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |  void grpc_chttp2_ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); }
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static const grpc_transport_vtable *get_vtable(void);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |                             const grpc_channel_args *channel_args,
 | 
	
		
			
				|  |  |                             grpc_endpoint *ep, bool is_client) {
 | 
	
	
		
			
				|  | @@ -257,7 +257,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |    GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) ==
 | 
	
		
			
				|  |  |               GRPC_CHTTP2_CLIENT_CONNECT_STRLEN);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  t->base.vtable = &vtable;
 | 
	
		
			
				|  |  | +  t->base.vtable = get_vtable();
 | 
	
		
			
				|  |  |    t->ep = ep;
 | 
	
		
			
				|  |  |    /* one ref is for destroy */
 | 
	
		
			
				|  |  |    gpr_ref_init(&t->refs, 1);
 | 
	
	
		
			
				|  | @@ -557,11 +557,6 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  GRPC_CLOSURE_INIT(&t->write_action, write_action, t,
 | 
	
		
			
				|  |  | -                    t->opt_target == GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT
 | 
	
		
			
				|  |  | -                        ? grpc_executor_scheduler
 | 
	
		
			
				|  |  | -                        : grpc_schedule_on_exec_ctx);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    t->ping_state.pings_before_data_required =
 | 
	
		
			
				|  |  |        t->ping_policy.max_pings_without_data;
 | 
	
		
			
				|  |  |    t->ping_state.is_delayed_ping_timer_set = false;
 | 
	
	
		
			
				|  | @@ -799,7 +794,7 @@ static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_chttp2_stream *grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |                                                        uint32_t id) {
 | 
	
		
			
				|  |  | -  return grpc_chttp2_stream_map_find(&t->stream_map, id);
 | 
	
		
			
				|  |  | +  return (grpc_chttp2_stream *)grpc_chttp2_stream_map_find(&t->stream_map, id);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  grpc_chttp2_stream *grpc_chttp2_parsing_accept_stream(grpc_exec_ctx *exec_ctx,
 | 
	
	
		
			
				|  | @@ -858,6 +853,7 @@ void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    switch (t->write_state) {
 | 
	
		
			
				|  |  |      case GRPC_CHTTP2_WRITE_STATE_IDLE:
 | 
	
		
			
				|  |  |        set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, reason);
 | 
	
		
			
				|  |  | +      t->is_first_write_in_batch = true;
 | 
	
		
			
				|  |  |        GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
 | 
	
		
			
				|  |  |        GRPC_CLOSURE_SCHED(
 | 
	
		
			
				|  |  |            exec_ctx,
 | 
	
	
		
			
				|  | @@ -876,46 +872,94 @@ void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    GPR_TIMER_END("grpc_chttp2_initiate_write", 0);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void grpc_chttp2_become_writable(
 | 
	
		
			
				|  |  | -    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s,
 | 
	
		
			
				|  |  | -    grpc_chttp2_stream_write_type stream_write_type, const char *reason) {
 | 
	
		
			
				|  |  | +void grpc_chttp2_become_writable(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                 grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  | +                                 grpc_chttp2_stream *s,
 | 
	
		
			
				|  |  | +                                 bool also_initiate_write, const char *reason) {
 | 
	
		
			
				|  |  |    if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s)) {
 | 
	
		
			
				|  |  |      GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:become");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  switch (stream_write_type) {
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK:
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED:
 | 
	
		
			
				|  |  | -      grpc_chttp2_initiate_write(exec_ctx, t, reason);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_STREAM_WRITE_INITIATE_UNCOVERED:
 | 
	
		
			
				|  |  | -      grpc_chttp2_initiate_write(exec_ctx, t, reason);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | +  if (also_initiate_write) {
 | 
	
		
			
				|  |  | +    grpc_chttp2_initiate_write(exec_ctx, t, reason);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static grpc_closure_scheduler *write_scheduler(grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  | +                                               bool early_results_scheduled,
 | 
	
		
			
				|  |  | +                                               bool partial_write) {
 | 
	
		
			
				|  |  | +  /* if it's not the first write in a batch, always offload to the executor:
 | 
	
		
			
				|  |  | +     we'll probably end up queuing against the kernel anyway, so we'll likely
 | 
	
		
			
				|  |  | +     get better latency overall if we switch writing work elsewhere and continue
 | 
	
		
			
				|  |  | +     with application work above */
 | 
	
		
			
				|  |  | +  if (!t->is_first_write_in_batch) {
 | 
	
		
			
				|  |  | +    return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  /* equivalently, if it's a partial write, we *know* we're going to be taking a
 | 
	
		
			
				|  |  | +     thread jump to write it because of the above, may as well do so
 | 
	
		
			
				|  |  | +     immediately */
 | 
	
		
			
				|  |  | +  if (partial_write) {
 | 
	
		
			
				|  |  | +    return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  switch (t->opt_target) {
 | 
	
		
			
				|  |  | +    case GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT:
 | 
	
		
			
				|  |  | +      /* executor gives us the largest probability of being able to batch a
 | 
	
		
			
				|  |  | +       * write with others on this transport */
 | 
	
		
			
				|  |  | +      return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT);
 | 
	
		
			
				|  |  | +    case GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY:
 | 
	
		
			
				|  |  | +      return grpc_schedule_on_exec_ctx;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GPR_UNREACHABLE_CODE(return NULL);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define WRITE_STATE_TUPLE_TO_INT(p, i) (2 * (int)(p) + (int)(i))
 | 
	
		
			
				|  |  | +static const char *begin_writing_desc(bool partial, bool inlined) {
 | 
	
		
			
				|  |  | +  switch (WRITE_STATE_TUPLE_TO_INT(partial, inlined)) {
 | 
	
		
			
				|  |  | +    case WRITE_STATE_TUPLE_TO_INT(false, false):
 | 
	
		
			
				|  |  | +      return "begin write in background";
 | 
	
		
			
				|  |  | +    case WRITE_STATE_TUPLE_TO_INT(false, true):
 | 
	
		
			
				|  |  | +      return "begin write in current thread";
 | 
	
		
			
				|  |  | +    case WRITE_STATE_TUPLE_TO_INT(true, false):
 | 
	
		
			
				|  |  | +      return "begin partial write in background";
 | 
	
		
			
				|  |  | +    case WRITE_STATE_TUPLE_TO_INT(true, true):
 | 
	
		
			
				|  |  | +      return "begin partial write in current thread";
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GPR_UNREACHABLE_CODE(return "bad state tuple");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *gt,
 | 
	
		
			
				|  |  |                                        grpc_error *error_ignored) {
 | 
	
		
			
				|  |  |    GPR_TIMER_BEGIN("write_action_begin_locked", 0);
 | 
	
		
			
				|  |  |    grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt;
 | 
	
		
			
				|  |  |    GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE);
 | 
	
		
			
				|  |  | -  switch (t->closed ? GRPC_CHTTP2_NOTHING_TO_WRITE
 | 
	
		
			
				|  |  | -                    : grpc_chttp2_begin_write(exec_ctx, t)) {
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_NOTHING_TO_WRITE:
 | 
	
		
			
				|  |  | -      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE,
 | 
	
		
			
				|  |  | -                      "begin writing nothing");
 | 
	
		
			
				|  |  | -      GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing");
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_PARTIAL_WRITE:
 | 
	
		
			
				|  |  | -      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE,
 | 
	
		
			
				|  |  | -                      "begin writing partial");
 | 
	
		
			
				|  |  | -      GRPC_CLOSURE_SCHED(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case GRPC_CHTTP2_FULL_WRITE:
 | 
	
		
			
				|  |  | -      set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
 | 
	
		
			
				|  |  | -                      "begin writing");
 | 
	
		
			
				|  |  | -      GRPC_CLOSURE_SCHED(exec_ctx, &t->write_action, GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | +  grpc_chttp2_begin_write_result r;
 | 
	
		
			
				|  |  | +  if (t->closed) {
 | 
	
		
			
				|  |  | +    r.writing = false;
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    r = grpc_chttp2_begin_write(exec_ctx, t);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (r.writing) {
 | 
	
		
			
				|  |  | +    if (r.partial) {
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HTTP2_PARTIAL_WRITES(exec_ctx);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (!t->is_first_write_in_batch) {
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HTTP2_WRITES_CONTINUED(exec_ctx);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_closure_scheduler *scheduler =
 | 
	
		
			
				|  |  | +        write_scheduler(t, r.early_results_scheduled, r.partial);
 | 
	
		
			
				|  |  | +    if (scheduler != grpc_schedule_on_exec_ctx) {
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED(exec_ctx);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    set_write_state(
 | 
	
		
			
				|  |  | +        exec_ctx, t, r.partial ? GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE
 | 
	
		
			
				|  |  | +                               : GRPC_CHTTP2_WRITE_STATE_WRITING,
 | 
	
		
			
				|  |  | +        begin_writing_desc(r.partial, scheduler == grpc_schedule_on_exec_ctx));
 | 
	
		
			
				|  |  | +    GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_INIT(&t->write_action,
 | 
	
		
			
				|  |  | +                                                   write_action, t, scheduler),
 | 
	
		
			
				|  |  | +                       GRPC_ERROR_NONE);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE,
 | 
	
		
			
				|  |  | +                    "begin writing nothing");
 | 
	
		
			
				|  |  | +    GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    GPR_TIMER_END("write_action_begin_locked", 0);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -958,7 +1002,8 @@ static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp,
 | 
	
		
			
				|  |  |      case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE:
 | 
	
		
			
				|  |  |        GPR_TIMER_MARK("state=writing_stale_no_poller", 0);
 | 
	
		
			
				|  |  |        set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING,
 | 
	
		
			
				|  |  | -                      "continue writing [!covered]");
 | 
	
		
			
				|  |  | +                      "continue writing");
 | 
	
		
			
				|  |  | +      t->is_first_write_in_batch = false;
 | 
	
		
			
				|  |  |        GRPC_CHTTP2_REF_TRANSPORT(t, "writing");
 | 
	
		
			
				|  |  |        GRPC_CLOSURE_RUN(
 | 
	
		
			
				|  |  |            exec_ctx,
 | 
	
	
		
			
				|  | @@ -1060,9 +1105,7 @@ static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      grpc_chttp2_stream_map_add(&t->stream_map, s->id, s);
 | 
	
		
			
				|  |  |      post_destructive_reclaimer(exec_ctx, t);
 | 
	
		
			
				|  |  | -    grpc_chttp2_become_writable(exec_ctx, t, s,
 | 
	
		
			
				|  |  | -                                GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED,
 | 
	
		
			
				|  |  | -                                "new_stream");
 | 
	
		
			
				|  |  | +    grpc_chttp2_become_writable(exec_ctx, t, s, true, "new_stream");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    /* cancel out streams that will never be started */
 | 
	
		
			
				|  |  |    while (t->next_stream_id >= MAX_CLIENT_STREAM_ID &&
 | 
	
	
		
			
				|  | @@ -1111,12 +1154,14 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
 | 
	
		
			
				|  |  |    if (GRPC_TRACER_ON(grpc_http_trace)) {
 | 
	
		
			
				|  |  |      const char *errstr = grpc_error_string(error);
 | 
	
		
			
				|  |  | -    gpr_log(GPR_DEBUG,
 | 
	
		
			
				|  |  | -            "complete_closure_step: %p refs=%d flags=0x%04x desc=%s err=%s",
 | 
	
		
			
				|  |  | -            closure,
 | 
	
		
			
				|  |  | -            (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT),
 | 
	
		
			
				|  |  | -            (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT),
 | 
	
		
			
				|  |  | -            desc, errstr);
 | 
	
		
			
				|  |  | +    gpr_log(
 | 
	
		
			
				|  |  | +        GPR_DEBUG,
 | 
	
		
			
				|  |  | +        "complete_closure_step: t=%p %p refs=%d flags=0x%04x desc=%s err=%s "
 | 
	
		
			
				|  |  | +        "write_state=%s",
 | 
	
		
			
				|  |  | +        t, closure,
 | 
	
		
			
				|  |  | +        (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT),
 | 
	
		
			
				|  |  | +        (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT), desc,
 | 
	
		
			
				|  |  | +        errstr, write_state_name(t->write_state));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (error != GRPC_ERROR_NONE) {
 | 
	
		
			
				|  |  |      if (closure->error_data.error == GRPC_ERROR_NONE) {
 | 
	
	
		
			
				|  | @@ -1157,9 +1202,7 @@ static void maybe_become_writable_due_to_send_msg(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                                                    grpc_chttp2_stream *s) {
 | 
	
		
			
				|  |  |    if (s->id != 0 && (!s->write_buffering ||
 | 
	
		
			
				|  |  |                       s->flow_controlled_buffer.length > t->write_buffer_size)) {
 | 
	
		
			
				|  |  | -    grpc_chttp2_become_writable(exec_ctx, t, s,
 | 
	
		
			
				|  |  | -                                GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED,
 | 
	
		
			
				|  |  | -                                "op.send_message");
 | 
	
		
			
				|  |  | +    grpc_chttp2_become_writable(exec_ctx, t, s, true, "op.send_message");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1198,8 +1241,12 @@ static void continue_fetching_send_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |          cb->call_at_byte = notify_offset;
 | 
	
		
			
				|  |  |          cb->closure = s->fetching_send_message_finished;
 | 
	
		
			
				|  |  |          s->fetching_send_message_finished = NULL;
 | 
	
		
			
				|  |  | -        cb->next = s->on_write_finished_cbs;
 | 
	
		
			
				|  |  | -        s->on_write_finished_cbs = cb;
 | 
	
		
			
				|  |  | +        grpc_chttp2_write_cb **list =
 | 
	
		
			
				|  |  | +            s->fetching_send_message->flags & GRPC_WRITE_THROUGH
 | 
	
		
			
				|  |  | +                ? &s->on_write_finished_cbs
 | 
	
		
			
				|  |  | +                : &s->on_flow_controlled_cbs;
 | 
	
		
			
				|  |  | +        cb->next = *list;
 | 
	
		
			
				|  |  | +        *list = cb;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        s->fetching_send_message = NULL;
 | 
	
		
			
				|  |  |        return; /* early out */
 | 
	
	
		
			
				|  | @@ -1357,14 +1404,13 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |          } else {
 | 
	
		
			
				|  |  |            GPR_ASSERT(s->id != 0);
 | 
	
		
			
				|  |  | -          grpc_chttp2_stream_write_type write_type =
 | 
	
		
			
				|  |  | -              GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED;
 | 
	
		
			
				|  |  | +          bool initiate_write = true;
 | 
	
		
			
				|  |  |            if (op->send_message &&
 | 
	
		
			
				|  |  |                (op->payload->send_message.send_message->flags &
 | 
	
		
			
				|  |  |                 GRPC_WRITE_BUFFER_HINT)) {
 | 
	
		
			
				|  |  | -            write_type = GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK;
 | 
	
		
			
				|  |  | +            initiate_write = false;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  | -          grpc_chttp2_become_writable(exec_ctx, t, s, write_type,
 | 
	
		
			
				|  |  | +          grpc_chttp2_become_writable(exec_ctx, t, s, initiate_write,
 | 
	
		
			
				|  |  |                                        "op.send_initial_metadata");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        } else {
 | 
	
	
		
			
				|  | @@ -1473,8 +1519,7 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op,
 | 
	
		
			
				|  |  |        } else if (s->id != 0) {
 | 
	
		
			
				|  |  |          /* TODO(ctiller): check if there's flow control for any outstanding
 | 
	
		
			
				|  |  |             bytes before going writable */
 | 
	
		
			
				|  |  | -        grpc_chttp2_become_writable(exec_ctx, t, s,
 | 
	
		
			
				|  |  | -                                    GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED,
 | 
	
		
			
				|  |  | +        grpc_chttp2_become_writable(exec_ctx, t, s, true,
 | 
	
		
			
				|  |  |                                      "op.send_trailing_metadata");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -1999,6 +2044,21 @@ static grpc_error *removal_error(grpc_error *extra_error, grpc_chttp2_stream *s,
 | 
	
		
			
				|  |  |    return error;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void flush_write_list(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  | +                             grpc_chttp2_stream *s, grpc_chttp2_write_cb **list,
 | 
	
		
			
				|  |  | +                             grpc_error *error) {
 | 
	
		
			
				|  |  | +  while (*list) {
 | 
	
		
			
				|  |  | +    grpc_chttp2_write_cb *cb = *list;
 | 
	
		
			
				|  |  | +    *list = cb->next;
 | 
	
		
			
				|  |  | +    grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure,
 | 
	
		
			
				|  |  | +                                      GRPC_ERROR_REF(error),
 | 
	
		
			
				|  |  | +                                      "on_write_finished_cb");
 | 
	
		
			
				|  |  | +    cb->next = t->write_cb_pool;
 | 
	
		
			
				|  |  | +    t->write_cb_pool = cb;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                                       grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |                                       grpc_chttp2_stream *s, grpc_error *error) {
 | 
	
	
		
			
				|  | @@ -2018,16 +2078,9 @@ void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |    grpc_chttp2_complete_closure_step(
 | 
	
		
			
				|  |  |        exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_REF(error),
 | 
	
		
			
				|  |  |        "fetching_send_message_finished");
 | 
	
		
			
				|  |  | -  while (s->on_write_finished_cbs) {
 | 
	
		
			
				|  |  | -    grpc_chttp2_write_cb *cb = s->on_write_finished_cbs;
 | 
	
		
			
				|  |  | -    s->on_write_finished_cbs = cb->next;
 | 
	
		
			
				|  |  | -    grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure,
 | 
	
		
			
				|  |  | -                                      GRPC_ERROR_REF(error),
 | 
	
		
			
				|  |  | -                                      "on_write_finished_cb");
 | 
	
		
			
				|  |  | -    cb->next = t->write_cb_pool;
 | 
	
		
			
				|  |  | -    t->write_cb_pool = cb;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +  flush_write_list(exec_ctx, t, s, &s->on_write_finished_cbs,
 | 
	
		
			
				|  |  | +                   GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  | +  flush_write_list(exec_ctx, t, s, &s->on_flow_controlled_cbs, error);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx,
 | 
	
	
		
			
				|  | @@ -2271,13 +2324,11 @@ void grpc_chttp2_act_on_flowctl_action(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |      case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED:
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY:
 | 
	
		
			
				|  |  | -      grpc_chttp2_become_writable(exec_ctx, t, s,
 | 
	
		
			
				|  |  | -                                  GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED,
 | 
	
		
			
				|  |  | +      grpc_chttp2_become_writable(exec_ctx, t, s, true,
 | 
	
		
			
				|  |  |                                    "immediate stream flowctl");
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |      case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE:
 | 
	
		
			
				|  |  | -      grpc_chttp2_become_writable(exec_ctx, t, s,
 | 
	
		
			
				|  |  | -                                  GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK,
 | 
	
		
			
				|  |  | +      grpc_chttp2_become_writable(exec_ctx, t, s, false,
 | 
	
		
			
				|  |  |                                    "queue stream flowctl");
 | 
	
		
			
				|  |  |        break;
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -2390,9 +2441,7 @@ static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp,
 | 
	
		
			
				|  |  |        if (t->flow_control.initial_window_update > 0) {
 | 
	
		
			
				|  |  |          grpc_chttp2_stream *s;
 | 
	
		
			
				|  |  |          while (grpc_chttp2_list_pop_stalled_by_stream(t, &s)) {
 | 
	
		
			
				|  |  | -          grpc_chttp2_become_writable(
 | 
	
		
			
				|  |  | -              exec_ctx, t, s, GRPC_CHTTP2_STREAM_WRITE_INITIATE_UNCOVERED,
 | 
	
		
			
				|  |  | -              "unstalled");
 | 
	
		
			
				|  |  | +          grpc_chttp2_become_writable(exec_ctx, t, s, true, "unstalled");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        t->flow_control.initial_window_update = 0;
 | 
	
	
		
			
				|  | @@ -2983,6 +3032,8 @@ static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream),
 | 
	
		
			
				|  |  |                                               destroy_transport,
 | 
	
		
			
				|  |  |                                               chttp2_get_endpoint};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static const grpc_transport_vtable *get_vtable(void) { return &vtable; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  grpc_transport *grpc_create_chttp2_transport(
 | 
	
		
			
				|  |  |      grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
 | 
	
		
			
				|  |  |      grpc_endpoint *ep, int is_client) {
 |