|  | @@ -146,12 +146,36 @@ cdef _cancel(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  cdef _next_call_event(
 | 
	
		
			
				|  |  |      _ChannelState channel_state, grpc_completion_queue *c_completion_queue,
 | 
	
		
			
				|  |  | -    on_success, deadline):
 | 
	
		
			
				|  |  | -  tag, event = _latent_event(c_completion_queue, deadline)
 | 
	
		
			
				|  |  | -  with channel_state.condition:
 | 
	
		
			
				|  |  | -    on_success(tag)
 | 
	
		
			
				|  |  | -    channel_state.condition.notify_all()
 | 
	
		
			
				|  |  | -  return event
 | 
	
		
			
				|  |  | +    on_success, on_failure, deadline):
 | 
	
		
			
				|  |  | +  """Block on the next event out of the completion queue.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  On success, `on_success` will be invoked with the tag taken from the CQ.
 | 
	
		
			
				|  |  | +  In the case of a failure due to an exception raised in a signal handler,
 | 
	
		
			
				|  |  | +  `on_failure` will be invoked with no arguments. Note that this situation
 | 
	
		
			
				|  |  | +  can only occur on the main thread.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Args:
 | 
	
		
			
				|  |  | +    channel_state: The state for the channel on which the RPC is running.
 | 
	
		
			
				|  |  | +    c_completion_queue: The CQ which will be polled.
 | 
	
		
			
				|  |  | +    on_success: A callable object to be invoked upon successful receipt of a
 | 
	
		
			
				|  |  | +      tag from the CQ.
 | 
	
		
			
				|  |  | +    on_failure: A callable object to be invoked in case a Python exception is
 | 
	
		
			
				|  |  | +      raised from a signal handler during polling.
 | 
	
		
			
				|  |  | +    deadline: The point after which the RPC will time out.
 | 
	
		
			
				|  |  | +  """
 | 
	
		
			
				|  |  | +  try:
 | 
	
		
			
				|  |  | +    tag, event = _latent_event(c_completion_queue, deadline)
 | 
	
		
			
				|  |  | +  # NOTE(rbellevi): This broad except enables us to clean up resources before
 | 
	
		
			
				|  |  | +  # propagating any exceptions raised by signal handlers to the application.
 | 
	
		
			
				|  |  | +  except:
 | 
	
		
			
				|  |  | +    if on_failure is not None:
 | 
	
		
			
				|  |  | +      on_failure()
 | 
	
		
			
				|  |  | +    raise
 | 
	
		
			
				|  |  | +  else:
 | 
	
		
			
				|  |  | +    with channel_state.condition:
 | 
	
		
			
				|  |  | +      on_success(tag)
 | 
	
		
			
				|  |  | +      channel_state.condition.notify_all()
 | 
	
		
			
				|  |  | +    return event
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  # TODO(https://github.com/grpc/grpc/issues/14569): This could be a lot simpler.
 | 
	
	
		
			
				|  | @@ -307,8 +331,14 @@ cdef class SegregatedCall:
 | 
	
		
			
				|  |  |      def on_success(tag):
 | 
	
		
			
				|  |  |        _process_segregated_call_tag(
 | 
	
		
			
				|  |  |          self._channel_state, self._call_state, self._c_completion_queue, tag)
 | 
	
		
			
				|  |  | +    def on_failure():
 | 
	
		
			
				|  |  | +      self._call_state.due.clear()
 | 
	
		
			
				|  |  | +      grpc_call_unref(self._call_state.c_call)
 | 
	
		
			
				|  |  | +      self._call_state.c_call = NULL
 | 
	
		
			
				|  |  | +      self._channel_state.segregated_call_states.remove(self._call_state)
 | 
	
		
			
				|  |  | +      _destroy_c_completion_queue(self._c_completion_queue)
 | 
	
		
			
				|  |  |      return _next_call_event(
 | 
	
		
			
				|  |  | -        self._channel_state, self._c_completion_queue, on_success, None)
 | 
	
		
			
				|  |  | +        self._channel_state, self._c_completion_queue, on_success, on_failure, None)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  cdef SegregatedCall _segregated_call(
 | 
	
	
		
			
				|  | @@ -461,8 +491,11 @@ cdef class Channel:
 | 
	
		
			
				|  |  |        queue_deadline = time.time() + 1.0
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  |        queue_deadline = None
 | 
	
		
			
				|  |  | +    # NOTE(gnossen): It is acceptable for on_failure to be None here because
 | 
	
		
			
				|  |  | +    # failure conditions can only ever happen on the main thread and this
 | 
	
		
			
				|  |  | +    # method is only ever invoked on the channel spin thread.
 | 
	
		
			
				|  |  |      return _next_call_event(self._state, self._state.c_call_completion_queue,
 | 
	
		
			
				|  |  | -                            on_success, queue_deadline)
 | 
	
		
			
				|  |  | +                            on_success, None, queue_deadline)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def segregated_call(
 | 
	
		
			
				|  |  |        self, int flags, method, host, object deadline, object metadata,
 |