|  | @@ -237,6 +237,9 @@ struct transport {
 | 
	
		
			
				|  |  |    /* state for a stream that's not yet been created */
 | 
	
		
			
				|  |  |    grpc_stream_op_buffer new_stream_sopb;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  /* stream ops that need to be destroyed, but outside of the lock */
 | 
	
		
			
				|  |  | +  grpc_stream_op_buffer nuke_later_sopb;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /* active parser */
 | 
	
		
			
				|  |  |    void *parser_data;
 | 
	
		
			
				|  |  |    stream *incoming_stream;
 | 
	
	
		
			
				|  | @@ -370,6 +373,8 @@ static void unref_transport(transport *t) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    gpr_free(t->pending_goaways);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  grpc_sopb_destroy(&t->nuke_later_sopb);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    gpr_free(t);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -416,6 +421,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
 | 
	
		
			
				|  |  |    t->cap_pending_goaways = 0;
 | 
	
		
			
				|  |  |    gpr_slice_buffer_init(&t->outbuf);
 | 
	
		
			
				|  |  |    gpr_slice_buffer_init(&t->qbuf);
 | 
	
		
			
				|  |  | +  grpc_sopb_init(&t->nuke_later_sopb);
 | 
	
		
			
				|  |  |    if (is_client) {
 | 
	
		
			
				|  |  |      gpr_slice_buffer_add(&t->qbuf,
 | 
	
		
			
				|  |  |                           gpr_slice_from_copied_string(CLIENT_CONNECT_STRING));
 | 
	
	
		
			
				|  | @@ -555,6 +561,11 @@ static int init_stream(grpc_transport *gt, grpc_stream *gs,
 | 
	
		
			
				|  |  |    return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void schedule_nuke_sopb(transport *t, grpc_stream_op_buffer *sopb) {
 | 
	
		
			
				|  |  | +  grpc_sopb_append(&t->nuke_later_sopb, sopb->ops, sopb->nops);
 | 
	
		
			
				|  |  | +  sopb->nops = 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
 | 
	
		
			
				|  |  |    transport *t = (transport *)gt;
 | 
	
		
			
				|  |  |    stream *s = (stream *)gs;
 | 
	
	
		
			
				|  | @@ -681,6 +692,11 @@ static void unlock(transport *t) {
 | 
	
		
			
				|  |  |    int i;
 | 
	
		
			
				|  |  |    pending_goaway *goaways = NULL;
 | 
	
		
			
				|  |  |    grpc_endpoint *ep = t->ep;
 | 
	
		
			
				|  |  | +  grpc_stream_op_buffer nuke_now = t->nuke_later_sopb;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (nuke_now.nops) {
 | 
	
		
			
				|  |  | +    memset(&t->nuke_later_sopb, 0, sizeof(t->nuke_later_sopb));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* see if we need to trigger a write - and if so, get the data ready */
 | 
	
		
			
				|  |  |    if (ep && !t->writing) {
 | 
	
	
		
			
				|  | @@ -750,6 +766,10 @@ static void unlock(transport *t) {
 | 
	
		
			
				|  |  |      unref_transport(t);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  if (nuke_now.nops) {
 | 
	
		
			
				|  |  | +    grpc_sopb_destroy(&nuke_now);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    gpr_free(goaways);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1006,9 +1026,9 @@ static void cancel_stream_inner(transport *t, stream *s, gpr_uint32 id,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (s) {
 | 
	
		
			
				|  |  |      /* clear out any unreported input & output: nobody cares anymore */
 | 
	
		
			
				|  |  | -    grpc_sopb_reset(&s->parser.incoming_sopb);
 | 
	
		
			
				|  |  |      had_outgoing = s->outgoing_sopb.nops != 0;
 | 
	
		
			
				|  |  | -    grpc_sopb_reset(&s->outgoing_sopb);
 | 
	
		
			
				|  |  | +    schedule_nuke_sopb(t, &s->parser.incoming_sopb);
 | 
	
		
			
				|  |  | +    schedule_nuke_sopb(t, &s->outgoing_sopb);
 | 
	
		
			
				|  |  |      if (s->cancelled) {
 | 
	
		
			
				|  |  |        send_rst = 0;
 | 
	
		
			
				|  |  |      } else if (!s->read_closed || !s->sent_write_closed || had_outgoing) {
 | 
	
	
		
			
				|  | @@ -1518,7 +1538,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      dts_fh_0:
 | 
	
		
			
				|  |  |      case DTS_FH_0:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_frame_size = ((gpr_uint32) * cur) << 16;
 | 
	
		
			
				|  |  | +      t->incoming_frame_size = ((gpr_uint32)*cur) << 16;
 | 
	
		
			
				|  |  |        if (++cur == end) {
 | 
	
		
			
				|  |  |          t->deframe_state = DTS_FH_1;
 | 
	
		
			
				|  |  |          return 1;
 | 
	
	
		
			
				|  | @@ -1526,7 +1546,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      /* fallthrough */
 | 
	
		
			
				|  |  |      case DTS_FH_1:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_frame_size |= ((gpr_uint32) * cur) << 8;
 | 
	
		
			
				|  |  | +      t->incoming_frame_size |= ((gpr_uint32)*cur) << 8;
 | 
	
		
			
				|  |  |        if (++cur == end) {
 | 
	
		
			
				|  |  |          t->deframe_state = DTS_FH_2;
 | 
	
		
			
				|  |  |          return 1;
 | 
	
	
		
			
				|  | @@ -1558,7 +1578,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      /* fallthrough */
 | 
	
		
			
				|  |  |      case DTS_FH_5:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_stream_id = (((gpr_uint32) * cur) << 24) & 0x7f;
 | 
	
		
			
				|  |  | +      t->incoming_stream_id = (((gpr_uint32)*cur) << 24) & 0x7f;
 | 
	
		
			
				|  |  |        if (++cur == end) {
 | 
	
		
			
				|  |  |          t->deframe_state = DTS_FH_6;
 | 
	
		
			
				|  |  |          return 1;
 | 
	
	
		
			
				|  | @@ -1566,7 +1586,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      /* fallthrough */
 | 
	
		
			
				|  |  |      case DTS_FH_6:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_stream_id |= ((gpr_uint32) * cur) << 16;
 | 
	
		
			
				|  |  | +      t->incoming_stream_id |= ((gpr_uint32)*cur) << 16;
 | 
	
		
			
				|  |  |        if (++cur == end) {
 | 
	
		
			
				|  |  |          t->deframe_state = DTS_FH_7;
 | 
	
		
			
				|  |  |          return 1;
 | 
	
	
		
			
				|  | @@ -1574,7 +1594,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      /* fallthrough */
 | 
	
		
			
				|  |  |      case DTS_FH_7:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_stream_id |= ((gpr_uint32) * cur) << 8;
 | 
	
		
			
				|  |  | +      t->incoming_stream_id |= ((gpr_uint32)*cur) << 8;
 | 
	
		
			
				|  |  |        if (++cur == end) {
 | 
	
		
			
				|  |  |          t->deframe_state = DTS_FH_8;
 | 
	
		
			
				|  |  |          return 1;
 | 
	
	
		
			
				|  | @@ -1582,7 +1602,7 @@ static int process_read(transport *t, gpr_slice slice) {
 | 
	
		
			
				|  |  |      /* fallthrough */
 | 
	
		
			
				|  |  |      case DTS_FH_8:
 | 
	
		
			
				|  |  |        GPR_ASSERT(cur < end);
 | 
	
		
			
				|  |  | -      t->incoming_stream_id |= ((gpr_uint32) * cur);
 | 
	
		
			
				|  |  | +      t->incoming_stream_id |= ((gpr_uint32)*cur);
 | 
	
		
			
				|  |  |        t->deframe_state = DTS_FRAME;
 | 
	
		
			
				|  |  |        if (!init_frame_parser(t)) {
 | 
	
		
			
				|  |  |          return 0;
 | 
	
	
		
			
				|  | @@ -1738,9 +1758,9 @@ static void add_to_pollset(grpc_transport *gt, grpc_pollset *pollset) {
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static const grpc_transport_vtable vtable = {
 | 
	
		
			
				|  |  | -    sizeof(stream),  init_stream,    send_batch,       set_allow_window_updates,
 | 
	
		
			
				|  |  | -    add_to_pollset,  destroy_stream, abort_stream,     goaway,
 | 
	
		
			
				|  |  | -    close_transport, send_ping,      destroy_transport};
 | 
	
		
			
				|  |  | +    sizeof(stream), init_stream, send_batch, set_allow_window_updates,
 | 
	
		
			
				|  |  | +    add_to_pollset, destroy_stream, abort_stream, goaway, close_transport,
 | 
	
		
			
				|  |  | +    send_ping, destroy_transport};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
 | 
	
		
			
				|  |  |                                    void *arg,
 |