|
|
|
@ -25,44 +25,31 @@ cdef class _HandlerCallDetails: |
|
|
|
|
class _ServicerContextPlaceHolder(object): pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class CallbackFailureHandler: |
|
|
|
|
cdef str _c_core_api |
|
|
|
|
cdef class _CallbackFailureHandler: |
|
|
|
|
cdef str _core_function_name |
|
|
|
|
cdef object _error_details |
|
|
|
|
cdef object _exception_type |
|
|
|
|
cdef object _callback # Callable[[Future], None] |
|
|
|
|
|
|
|
|
|
def __cinit__(self, |
|
|
|
|
str c_core_api="", |
|
|
|
|
object error_details="UNKNOWN", |
|
|
|
|
object exception_type=RuntimeError, |
|
|
|
|
object callback=None): |
|
|
|
|
"""Handles failure by raising exception or execute a callbcak. |
|
|
|
|
|
|
|
|
|
The callback accepts a future, returns nothing. The callback is |
|
|
|
|
expected to finish the future either "set_result" or "set_exception". |
|
|
|
|
""" |
|
|
|
|
if callback is None: |
|
|
|
|
self._c_core_api = c_core_api |
|
|
|
|
self._error_details = error_details |
|
|
|
|
self._exception_type = exception_type |
|
|
|
|
self._callback = self._raise_exception |
|
|
|
|
else: |
|
|
|
|
self._callback = callback |
|
|
|
|
str core_function_name, |
|
|
|
|
object error_details, |
|
|
|
|
object exception_type): |
|
|
|
|
"""Handles failure by raising exception.""" |
|
|
|
|
self._core_function_name = core_function_name |
|
|
|
|
self._error_details = error_details |
|
|
|
|
self._exception_type = exception_type |
|
|
|
|
|
|
|
|
|
def _raise_exception(self, object future): |
|
|
|
|
cdef handle(self, object future): |
|
|
|
|
future.set_exception(self._exception_type( |
|
|
|
|
'Failed "%s": %s' % (self._c_core_api, self._error_details) |
|
|
|
|
'Failed "%s": %s' % (self._core_function_name, self._error_details) |
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
cdef handle(self, object future): |
|
|
|
|
self._callback(future) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO(https://github.com/grpc/grpc/issues/20669) |
|
|
|
|
# Apply this to the client-side |
|
|
|
|
cdef class CallbackWrapper: |
|
|
|
|
|
|
|
|
|
def __cinit__(self, object future, CallbackFailureHandler failure_handler): |
|
|
|
|
def __cinit__(self, object future, _CallbackFailureHandler failure_handler): |
|
|
|
|
self.context.functor.functor_run = self.functor_run |
|
|
|
|
self.context.waiter = <cpython.PyObject*>future |
|
|
|
|
self.context.failure_handler = <cpython.PyObject*>failure_handler |
|
|
|
@ -77,7 +64,7 @@ cdef class CallbackWrapper: |
|
|
|
|
int success): |
|
|
|
|
cdef CallbackContext *context = <CallbackContext *>functor |
|
|
|
|
if succeed == 0: |
|
|
|
|
(<CallbackFailureHandler>context.failure_handler).handle( |
|
|
|
|
(<_CallbackFailureHandler>context.failure_handler).handle( |
|
|
|
|
<object>context.waiter) |
|
|
|
|
else: |
|
|
|
|
(<object>context.waiter).set_result(None) |
|
|
|
@ -127,7 +114,7 @@ async def callback_start_batch(RPCState rpc_state, |
|
|
|
|
cdef object future = loop.create_future() |
|
|
|
|
cdef CallbackWrapper wrapper = CallbackWrapper( |
|
|
|
|
future, |
|
|
|
|
CallbackFailureHandler('callback_start_batch', operations)) |
|
|
|
|
_CallbackFailureHandler('callback_start_batch', operations, RuntimeError)) |
|
|
|
|
# NOTE(lidiz) Without Py_INCREF, the wrapper object will be destructed |
|
|
|
|
# when calling "await". This is an over-optimization by Cython. |
|
|
|
|
cpython.Py_INCREF(wrapper) |
|
|
|
@ -206,7 +193,7 @@ async def _handle_rpc(list generic_handlers, RPCState rpc_state, object loop): |
|
|
|
|
|
|
|
|
|
class _RequestCallError(Exception): pass |
|
|
|
|
|
|
|
|
|
cdef CallbackFailureHandler REQUEST_CALL_FAILURE_HANDLER = CallbackFailureHandler( |
|
|
|
|
cdef _CallbackFailureHandler REQUEST_CALL_FAILURE_HANDLER = _CallbackFailureHandler( |
|
|
|
|
'grpc_server_request_call', 'server shutdown', _RequestCallError) |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -236,7 +223,10 @@ async def _server_call_request_call(Server server, |
|
|
|
|
return rpc_state |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef CallbackFailureHandler CQ_SHUTDOWN_FAILURE_HANDLER = CallbackFailureHandler('grpc_completion_queue_shutdown') |
|
|
|
|
cdef _CallbackFailureHandler CQ_SHUTDOWN_FAILURE_HANDLER = _CallbackFailureHandler( |
|
|
|
|
'grpc_completion_queue_shutdown', |
|
|
|
|
'Unknown', |
|
|
|
|
RuntimeError) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class _CallbackCompletionQueue: |
|
|
|
@ -261,14 +251,18 @@ cdef class _CallbackCompletionQueue: |
|
|
|
|
grpc_completion_queue_destroy(self._cq) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef CallbackFailureHandler SERVER_SHUTDOWN_FAILURE_HANDLER = CallbackFailureHandler('grpc_server_shutdown_and_notify') |
|
|
|
|
cdef _CallbackFailureHandler SERVER_SHUTDOWN_FAILURE_HANDLER = _CallbackFailureHandler( |
|
|
|
|
'grpc_server_shutdown_and_notify', |
|
|
|
|
'Unknown', |
|
|
|
|
RuntimeError) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class AioServer: |
|
|
|
|
|
|
|
|
|
def __init__(self, loop, thread_pool, generic_handlers, interceptors, |
|
|
|
|
options, maximum_concurrent_rpcs, compression): |
|
|
|
|
# C-Core objects won't be deallocated automatically. |
|
|
|
|
# NOTE(lidiz) Core objects won't be deallocated automatically. |
|
|
|
|
# If AioServer.shutdown is not called, those objects will leak. |
|
|
|
|
self._server = Server(options) |
|
|
|
|
self._cq = _CallbackCompletionQueue(loop) |
|
|
|
|
grpc_server_register_completion_queue( |
|
|
|
@ -311,7 +305,7 @@ cdef class AioServer: |
|
|
|
|
|
|
|
|
|
async def _server_main_loop(self, |
|
|
|
|
object server_started): |
|
|
|
|
self._server.start(backup_queue=False) |
|
|
|
|
self._server.start() |
|
|
|
|
cdef RPCState rpc_state |
|
|
|
|
server_started.set_result(True) |
|
|
|
|
|
|
|
|
@ -344,8 +338,10 @@ cdef class AioServer: |
|
|
|
|
await server_started |
|
|
|
|
|
|
|
|
|
async def _start_shutting_down(self): |
|
|
|
|
"""Prepares the server to shutting down (NOT coroutine-safe).""" |
|
|
|
|
# Starts the shutdown process. |
|
|
|
|
"""Prepares the server to shutting down. |
|
|
|
|
|
|
|
|
|
This coroutine function is NOT coroutine-safe. |
|
|
|
|
""" |
|
|
|
|
# The shutdown callback won't be called until there is no live RPC. |
|
|
|
|
grpc_server_shutdown_and_notify( |
|
|
|
|
self._server.c_server, |
|
|
|
@ -409,5 +405,10 @@ cdef class AioServer: |
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
"""Deallocation of Core objects are ensured by Python grpc.aio.Server. |
|
|
|
|
|
|
|
|
|
If the Cython representation is deallocated without underlying objects |
|
|
|
|
freed, raise an RuntimeError. |
|
|
|
|
""" |
|
|
|
|
if self._status != AIO_SERVER_STATUS_STOPPED: |
|
|
|
|
_LOGGER.error('__dealloc__ called on running server: %d', self._status) |
|
|
|
|
raise RuntimeError('__dealloc__ called on running server: %d', self._status) |
|
|
|
|