|  | @@ -58,6 +58,7 @@ typedef struct {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* the sending child (may be null) */
 | 
	
		
			
				|  |  |    grpc_child_channel *active_child;
 | 
	
		
			
				|  |  | +  grpc_mdctx *mdctx;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* calls waiting for a channel to be ready */
 | 
	
		
			
				|  |  |    call_data **waiting_children;
 | 
	
	
		
			
				|  | @@ -92,6 +93,10 @@ struct call_data {
 | 
	
		
			
				|  |  |        grpc_child_call *child_call;
 | 
	
		
			
				|  |  |      } active;
 | 
	
		
			
				|  |  |      grpc_transport_op waiting_op;
 | 
	
		
			
				|  |  | +    struct {
 | 
	
		
			
				|  |  | +      grpc_linked_mdelem status;
 | 
	
		
			
				|  |  | +      grpc_linked_mdelem details;
 | 
	
		
			
				|  |  | +    } cancelled;
 | 
	
		
			
				|  |  |    } s;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -185,12 +190,38 @@ static void remove_waiting_child(channel_data *chand, call_data *calld) {
 | 
	
		
			
				|  |  |    chand->waiting_child_count = new_count;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void send_up_cancelled_ops(grpc_call_element *elem) { abort(); }
 | 
	
		
			
				|  |  | +static void handle_op_after_cancellation(grpc_call_element *elem, grpc_transport_op *op) {
 | 
	
		
			
				|  |  | +  call_data *calld = elem->call_data;
 | 
	
		
			
				|  |  | +  channel_data *chand = elem->channel_data;
 | 
	
		
			
				|  |  | +  if (op->send_ops) {
 | 
	
		
			
				|  |  | +    op->on_done_send(op->send_user_data, 0);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (op->recv_ops) {
 | 
	
		
			
				|  |  | +    char status[GPR_LTOA_MIN_BUFSIZE];
 | 
	
		
			
				|  |  | +    grpc_metadata_batch mdb;
 | 
	
		
			
				|  |  | +    gpr_ltoa(GRPC_STATUS_CANCELLED, status);
 | 
	
		
			
				|  |  | +    calld->s.cancelled.status.md = grpc_mdelem_from_strings(chand->mdctx,
 | 
	
		
			
				|  |  | +      "grpc-status", status);
 | 
	
		
			
				|  |  | +    calld->s.cancelled.details.md = grpc_mdelem_from_strings(chand->mdctx,
 | 
	
		
			
				|  |  | +      "grpc-message", "Cancelled");
 | 
	
		
			
				|  |  | +    calld->s.cancelled.status.prev = calld->s.cancelled.details.next = NULL;
 | 
	
		
			
				|  |  | +    calld->s.cancelled.status.next = &calld->s.cancelled.details;
 | 
	
		
			
				|  |  | +    calld->s.cancelled.details.prev = &calld->s.cancelled.status;
 | 
	
		
			
				|  |  | +    mdb.list.head = &calld->s.cancelled.status;
 | 
	
		
			
				|  |  | +    mdb.list.tail = &calld->s.cancelled.details;
 | 
	
		
			
				|  |  | +    mdb.garbage.head = mdb.garbage.tail = NULL;
 | 
	
		
			
				|  |  | +    mdb.deadline = gpr_inf_future;
 | 
	
		
			
				|  |  | +    grpc_sopb_add_metadata(op->recv_ops, mdb);
 | 
	
		
			
				|  |  | +    *op->recv_state = GRPC_STREAM_CLOSED;
 | 
	
		
			
				|  |  | +    op->on_done_recv(op->recv_user_data, 1);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void cancel_rpc(grpc_call_element *elem, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |    call_data *calld = elem->call_data;
 | 
	
		
			
				|  |  |    channel_data *chand = elem->channel_data;
 | 
	
		
			
				|  |  |    grpc_call_element *child_elem;
 | 
	
		
			
				|  |  | +  grpc_transport_op waiting_op;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    gpr_mu_lock(&chand->mu);
 | 
	
		
			
				|  |  |    switch (calld->state) {
 | 
	
	
		
			
				|  | @@ -200,18 +231,21 @@ static void cancel_rpc(grpc_call_element *elem, grpc_transport_op *op) {
 | 
	
		
			
				|  |  |        child_elem->filter->start_transport_op(child_elem, op);
 | 
	
		
			
				|  |  |        return; /* early out */
 | 
	
		
			
				|  |  |      case CALL_WAITING:
 | 
	
		
			
				|  |  | +      waiting_op = calld->s.waiting_op;
 | 
	
		
			
				|  |  |        remove_waiting_child(chand, calld);
 | 
	
		
			
				|  |  |        calld->state = CALL_CANCELLED;
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&chand->mu);
 | 
	
		
			
				|  |  | -      send_up_cancelled_ops(elem);
 | 
	
		
			
				|  |  | +      handle_op_after_cancellation(elem, &waiting_op);
 | 
	
		
			
				|  |  | +      handle_op_after_cancellation(elem, op);
 | 
	
		
			
				|  |  |        return; /* early out */
 | 
	
		
			
				|  |  |      case CALL_CREATED:
 | 
	
		
			
				|  |  |        calld->state = CALL_CANCELLED;
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&chand->mu);
 | 
	
		
			
				|  |  | -      send_up_cancelled_ops(elem);
 | 
	
		
			
				|  |  | +      handle_op_after_cancellation(elem, op);
 | 
	
		
			
				|  |  |        return; /* early out */
 | 
	
		
			
				|  |  |      case CALL_CANCELLED:
 | 
	
		
			
				|  |  |        gpr_mu_unlock(&chand->mu);
 | 
	
		
			
				|  |  | +      handle_op_after_cancellation(elem, op);
 | 
	
		
			
				|  |  |        return; /* early out */
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    gpr_log(GPR_ERROR, "should never reach here");
 | 
	
	
		
			
				|  | @@ -232,6 +266,11 @@ static void cc_start_transport_op(grpc_call_element *elem,
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  if (calld->state == CALL_CANCELLED) {
 | 
	
		
			
				|  |  | +    handle_op_after_cancellation(elem, op);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    if (!calld->got_first_op) {
 | 
	
		
			
				|  |  |      calld->got_first_op = 1;
 | 
	
		
			
				|  |  |      start_rpc(elem, op);
 | 
	
	
		
			
				|  | @@ -371,6 +410,7 @@ static void init_channel_elem(grpc_channel_element *elem,
 | 
	
		
			
				|  |  |    chand->transport_setup = NULL;
 | 
	
		
			
				|  |  |    chand->transport_setup_initiated = 0;
 | 
	
		
			
				|  |  |    chand->args = grpc_channel_args_copy(args);
 | 
	
		
			
				|  |  | +  chand->mdctx = metadata_context;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Destructor for channel_data */
 |