|
|
|
@ -32,6 +32,9 @@ cdef class _AioCall: |
|
|
|
|
|
|
|
|
|
self._status_received = asyncio.Event(loop=self._loop) |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
self._destroy_grpc_call() |
|
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
|
class_name = self.__class__.__name__ |
|
|
|
|
id_ = id(self) |
|
|
|
@ -68,9 +71,13 @@ cdef class _AioCall: |
|
|
|
|
grpc_slice_unref(method_slice) |
|
|
|
|
|
|
|
|
|
cdef void _destroy_grpc_call(self): |
|
|
|
|
"""Destroys the corresponding Core object for this RPC.""" |
|
|
|
|
"""Destroys the corresponding Core object for this RPC. |
|
|
|
|
|
|
|
|
|
This method is idempotent. Multiple calls should not result in crashes. |
|
|
|
|
""" |
|
|
|
|
if self._grpc_call_wrapper.call != NULL: |
|
|
|
|
grpc_call_unref(self._grpc_call_wrapper.call) |
|
|
|
|
self._grpc_call_wrapper.call = NULL |
|
|
|
|
|
|
|
|
|
cdef AioRpcStatus _cancel_and_create_status(self, object cancellation_future): |
|
|
|
|
"""Cancels the RPC in C-Core, and return the final RPC status.""" |
|
|
|
@ -183,6 +190,7 @@ cdef class _AioCall: |
|
|
|
|
) |
|
|
|
|
status_observer(status) |
|
|
|
|
self._status_received.set() |
|
|
|
|
self._destroy_grpc_call() |
|
|
|
|
|
|
|
|
|
def _handle_cancellation_from_application(self, |
|
|
|
|
object cancellation_future, |
|
|
|
@ -190,9 +198,33 @@ cdef class _AioCall: |
|
|
|
|
def _cancellation_action(finished_future): |
|
|
|
|
status = self._cancel_and_create_status(finished_future) |
|
|
|
|
status_observer(status) |
|
|
|
|
self._status_received.set() |
|
|
|
|
self._destroy_grpc_call() |
|
|
|
|
|
|
|
|
|
cancellation_future.add_done_callback(_cancellation_action) |
|
|
|
|
|
|
|
|
|
async def _message_async_generator(self): |
|
|
|
|
cdef bytes received_message |
|
|
|
|
|
|
|
|
|
# Infinitely receiving messages, until: |
|
|
|
|
# * EOF, no more messages to read; |
|
|
|
|
# * The client application cancells; |
|
|
|
|
# * The server sends final status. |
|
|
|
|
while True: |
|
|
|
|
if self._status_received.is_set(): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
received_message = await _receive_message( |
|
|
|
|
self._grpc_call_wrapper, |
|
|
|
|
self._loop |
|
|
|
|
) |
|
|
|
|
if received_message is None: |
|
|
|
|
# The read operation failed, C-Core should explain why it fails |
|
|
|
|
await self._status_received.wait() |
|
|
|
|
return |
|
|
|
|
else: |
|
|
|
|
yield received_message |
|
|
|
|
|
|
|
|
|
async def unary_stream(self, |
|
|
|
|
bytes method, |
|
|
|
|
bytes request, |
|
|
|
@ -206,7 +238,6 @@ cdef class _AioCall: |
|
|
|
|
propagate the final status exception, then we have to raise it. |
|
|
|
|
Othersize, it would end normally and raise `StopAsyncIteration()`. |
|
|
|
|
""" |
|
|
|
|
cdef bytes received_message |
|
|
|
|
cdef tuple outbound_ops |
|
|
|
|
cdef Operation initial_metadata_op = SendInitialMetadataOperation( |
|
|
|
|
_EMPTY_METADATA, |
|
|
|
@ -223,45 +254,25 @@ cdef class _AioCall: |
|
|
|
|
send_close_op, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
# NOTE(lidiz) Not catching CancelledError here, because async |
|
|
|
|
# generators do not have "cancel" method. |
|
|
|
|
try: |
|
|
|
|
self._create_grpc_call(deadline, method) |
|
|
|
|
# Creates the grpc_call C-Core object, it needs to be deleted explicitly |
|
|
|
|
# through _destroy_grpc_call call in other methods. |
|
|
|
|
self._create_grpc_call(deadline, method) |
|
|
|
|
|
|
|
|
|
await callback_start_batch( |
|
|
|
|
self._grpc_call_wrapper, |
|
|
|
|
outbound_ops, |
|
|
|
|
self._loop) |
|
|
|
|
# Actually sends out the request message. |
|
|
|
|
await callback_start_batch(self._grpc_call_wrapper, |
|
|
|
|
outbound_ops, |
|
|
|
|
self._loop) |
|
|
|
|
|
|
|
|
|
# Peer may prematurely end this RPC at any point. We need a mechanism |
|
|
|
|
# that handles both the normal case and the error case. |
|
|
|
|
self._loop.create_task(self._handle_status_once_received(status_observer)) |
|
|
|
|
self._handle_cancellation_from_application(cancellation_future, |
|
|
|
|
status_observer) |
|
|
|
|
# Peer may prematurely end this RPC at any point. We need a mechanism |
|
|
|
|
# that handles both the normal case and the error case. |
|
|
|
|
self._loop.create_task(self._handle_status_once_received(status_observer)) |
|
|
|
|
self._handle_cancellation_from_application(cancellation_future, |
|
|
|
|
status_observer) |
|
|
|
|
|
|
|
|
|
# Receives initial metadata. |
|
|
|
|
initial_metadata_observer( |
|
|
|
|
await _receive_initial_metadata(self._grpc_call_wrapper, |
|
|
|
|
self._loop), |
|
|
|
|
) |
|
|
|
|
# Receives initial metadata. |
|
|
|
|
initial_metadata_observer( |
|
|
|
|
await _receive_initial_metadata(self._grpc_call_wrapper, |
|
|
|
|
self._loop), |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
# Infinitely receiving messages, until: |
|
|
|
|
# * EOF, no more messages to read; |
|
|
|
|
# * The client application cancells; |
|
|
|
|
# * The server sends final status. |
|
|
|
|
while True: |
|
|
|
|
if self._status_received.is_set(): |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
received_message = await _receive_message( |
|
|
|
|
self._grpc_call_wrapper, |
|
|
|
|
self._loop |
|
|
|
|
) |
|
|
|
|
if received_message is None: |
|
|
|
|
# The read operation failed, wait for status from C-Core. |
|
|
|
|
await self._status_received.wait() |
|
|
|
|
return |
|
|
|
|
else: |
|
|
|
|
yield received_message |
|
|
|
|
finally: |
|
|
|
|
self._destroy_grpc_call() |
|
|
|
|
return self._message_async_generator() |
|
|
|
|