mirror of https://github.com/grpc/grpc.git
[Aio] Remove custom IO manager support (#27090)
* [Aio] Remove custom IO manager support * Patchpull/27107/head
parent
5b7c25155e
commit
d3d5dec5b8
12 changed files with 4 additions and 763 deletions
@ -1,256 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
|
|
||||||
import platform |
|
||||||
|
|
||||||
from cpython cimport Py_INCREF, Py_DECREF |
|
||||||
from libc cimport string |
|
||||||
|
|
||||||
import socket as native_socket |
|
||||||
try: |
|
||||||
import ipaddress # CPython 3.3 and above |
|
||||||
except ImportError: |
|
||||||
pass |
|
||||||
|
|
||||||
cdef grpc_socket_vtable asyncio_socket_vtable |
|
||||||
cdef grpc_custom_resolver_vtable asyncio_resolver_vtable |
|
||||||
cdef grpc_custom_timer_vtable asyncio_timer_vtable |
|
||||||
cdef grpc_custom_poller_vtable asyncio_pollset_vtable |
|
||||||
cdef bint so_reuse_port |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_socket_init( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
int domain) with gil: |
|
||||||
socket = _AsyncioSocket.create(grpc_socket, None, None) |
|
||||||
Py_INCREF(socket) |
|
||||||
grpc_socket.impl = <void*>socket |
|
||||||
return <grpc_error_handle>0 |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_destroy(grpc_custom_socket* grpc_socket) with gil: |
|
||||||
Py_DECREF(<_AsyncioSocket>grpc_socket.impl) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_connect( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
const grpc_sockaddr* addr, |
|
||||||
size_t addr_len, |
|
||||||
grpc_custom_connect_callback connect_cb) with gil: |
|
||||||
host, port = sockaddr_to_tuple(addr, addr_len) |
|
||||||
socket = <_AsyncioSocket>grpc_socket.impl |
|
||||||
socket.connect(host, port, connect_cb) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_close( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
grpc_custom_close_callback close_cb) with gil: |
|
||||||
socket = (<_AsyncioSocket>grpc_socket.impl) |
|
||||||
socket.close() |
|
||||||
close_cb(grpc_socket) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_shutdown(grpc_custom_socket* grpc_socket) with gil: |
|
||||||
socket = (<_AsyncioSocket>grpc_socket.impl) |
|
||||||
socket.close() |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_write( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
grpc_slice_buffer* slice_buffer, |
|
||||||
grpc_custom_write_callback write_cb) with gil: |
|
||||||
socket = (<_AsyncioSocket>grpc_socket.impl) |
|
||||||
socket.write(slice_buffer, write_cb) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_read( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
char* buffer_, |
|
||||||
size_t length, |
|
||||||
grpc_custom_read_callback read_cb) with gil: |
|
||||||
socket = (<_AsyncioSocket>grpc_socket.impl) |
|
||||||
socket.read(buffer_, length, read_cb) |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_socket_getpeername( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
const grpc_sockaddr* addr, |
|
||||||
int* length) with gil: |
|
||||||
peer = (<_AsyncioSocket>grpc_socket.impl).peername() |
|
||||||
|
|
||||||
cdef grpc_resolved_address c_addr |
|
||||||
hostname = str_to_bytes(peer[0]) |
|
||||||
grpc_string_to_sockaddr(&c_addr, hostname, peer[1]) |
|
||||||
# TODO(https://github.com/grpc/grpc/issues/20684) Remove the memcpy |
|
||||||
string.memcpy(<void*>addr, <void*>c_addr.addr, c_addr.len) |
|
||||||
length[0] = c_addr.len |
|
||||||
return grpc_error_none() |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_socket_getsockname( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
const grpc_sockaddr* addr, |
|
||||||
int* length) with gil: |
|
||||||
"""Supplies sock_addr in add_socket_to_server.""" |
|
||||||
cdef grpc_resolved_address c_addr |
|
||||||
socket = (<_AsyncioSocket>grpc_socket.impl) |
|
||||||
if socket is None: |
|
||||||
peer = ('0.0.0.0', 0) |
|
||||||
else: |
|
||||||
peer = socket.sockname() |
|
||||||
hostname = str_to_bytes(peer[0]) |
|
||||||
grpc_string_to_sockaddr(&c_addr, hostname, peer[1]) |
|
||||||
# TODO(https://github.com/grpc/grpc/issues/20684) Remove the memcpy |
|
||||||
string.memcpy(<void*>addr, <void*>c_addr.addr, c_addr.len) |
|
||||||
length[0] = c_addr.len |
|
||||||
return grpc_error_none() |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_socket_listen(grpc_custom_socket* grpc_socket) with gil: |
|
||||||
(<_AsyncioSocket>grpc_socket.impl).listen() |
|
||||||
return grpc_error_none() |
|
||||||
|
|
||||||
|
|
||||||
def _asyncio_apply_socket_options(object s, int flags): |
|
||||||
# Turn SO_REUSEADDR on for TCP sockets; if we want to support UDS, we will |
|
||||||
# need to update this function. |
|
||||||
s.setsockopt(native_socket.SOL_SOCKET, native_socket.SO_REUSEADDR, 1) |
|
||||||
# SO_REUSEPORT only available in POSIX systems. |
|
||||||
if platform.system() != 'Windows': |
|
||||||
if GRPC_CUSTOM_SOCKET_OPT_SO_REUSEPORT & flags: |
|
||||||
s.setsockopt(native_socket.SOL_SOCKET, native_socket.SO_REUSEPORT, 1) |
|
||||||
s.setsockopt(native_socket.IPPROTO_TCP, native_socket.TCP_NODELAY, True) |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_socket_bind( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
const grpc_sockaddr* addr, |
|
||||||
size_t len, int flags) with gil: |
|
||||||
host, port = sockaddr_to_tuple(addr, len) |
|
||||||
try: |
|
||||||
ip = ipaddress.ip_address(host) |
|
||||||
if isinstance(ip, ipaddress.IPv6Address): |
|
||||||
family = native_socket.AF_INET6 |
|
||||||
else: |
|
||||||
family = native_socket.AF_INET |
|
||||||
|
|
||||||
socket = native_socket.socket(family=family) |
|
||||||
_asyncio_apply_socket_options(socket, flags) |
|
||||||
socket.bind((host, port)) |
|
||||||
except IOError as io_error: |
|
||||||
socket.close() |
|
||||||
return socket_error("bind", str(io_error)) |
|
||||||
else: |
|
||||||
aio_socket = _AsyncioSocket.create_with_py_socket(grpc_socket, socket) |
|
||||||
cpython.Py_INCREF(aio_socket) # Py_DECREF in asyncio_socket_destroy |
|
||||||
grpc_socket.impl = <void*>aio_socket |
|
||||||
return grpc_error_none() |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_socket_accept( |
|
||||||
grpc_custom_socket* grpc_socket, |
|
||||||
grpc_custom_socket* grpc_socket_client, |
|
||||||
grpc_custom_accept_callback accept_cb) with gil: |
|
||||||
(<_AsyncioSocket>grpc_socket.impl).accept(grpc_socket_client, accept_cb) |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error_handle asyncio_resolve( |
|
||||||
const char* host, |
|
||||||
const char* port, |
|
||||||
grpc_resolved_addresses** res) with gil: |
|
||||||
result = native_socket.getaddrinfo(host, port) |
|
||||||
res[0] = tuples_to_resolvaddr(result) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_resolve_async( |
|
||||||
grpc_custom_resolver* grpc_resolver, |
|
||||||
const char* host, |
|
||||||
const char* port) with gil: |
|
||||||
resolver = _AsyncioResolver.create(grpc_resolver) |
|
||||||
resolver.resolve(host, port) |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_timer_start(grpc_custom_timer* grpc_timer) with gil: |
|
||||||
timer = _AsyncioTimer.create(grpc_timer, grpc_timer.timeout_ms / 1000.0) |
|
||||||
grpc_timer.timer = <void*>timer |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_timer_stop(grpc_custom_timer* grpc_timer) with gil: |
|
||||||
# TODO(https://github.com/grpc/grpc/issues/22278) remove this if condition |
|
||||||
if grpc_timer.timer == NULL: |
|
||||||
return |
|
||||||
else: |
|
||||||
timer = <_AsyncioTimer>grpc_timer.timer |
|
||||||
timer.stop() |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_init_loop() with gil: |
|
||||||
pass |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_destroy_loop() with gil: |
|
||||||
pass |
|
||||||
|
|
||||||
|
|
||||||
cdef void asyncio_kick_loop() with gil: |
|
||||||
pass |
|
||||||
|
|
||||||
|
|
||||||
cdef grpc_error* asyncio_run_loop(size_t timeout_ms) with gil: |
|
||||||
return grpc_error_none() |
|
||||||
|
|
||||||
|
|
||||||
def _auth_plugin_callback_wrapper(object cb, |
|
||||||
str service_url, |
|
||||||
str method_name, |
|
||||||
object callback): |
|
||||||
get_working_loop().call_soon(cb, service_url, method_name, callback) |
|
||||||
|
|
||||||
|
|
||||||
def install_asyncio_iomgr(): |
|
||||||
# Auth plugins invoke user provided logic in another thread by default. We |
|
||||||
# need to override that behavior by registering the call to the event loop. |
|
||||||
set_async_callback_func(_auth_plugin_callback_wrapper) |
|
||||||
|
|
||||||
asyncio_resolver_vtable.resolve = asyncio_resolve |
|
||||||
asyncio_resolver_vtable.resolve_async = asyncio_resolve_async |
|
||||||
|
|
||||||
asyncio_socket_vtable.init = asyncio_socket_init |
|
||||||
asyncio_socket_vtable.connect = asyncio_socket_connect |
|
||||||
asyncio_socket_vtable.destroy = asyncio_socket_destroy |
|
||||||
asyncio_socket_vtable.shutdown = asyncio_socket_shutdown |
|
||||||
asyncio_socket_vtable.close = asyncio_socket_close |
|
||||||
asyncio_socket_vtable.write = asyncio_socket_write |
|
||||||
asyncio_socket_vtable.read = asyncio_socket_read |
|
||||||
asyncio_socket_vtable.getpeername = asyncio_socket_getpeername |
|
||||||
asyncio_socket_vtable.getsockname = asyncio_socket_getsockname |
|
||||||
asyncio_socket_vtable.bind = asyncio_socket_bind |
|
||||||
asyncio_socket_vtable.listen = asyncio_socket_listen |
|
||||||
asyncio_socket_vtable.accept = asyncio_socket_accept |
|
||||||
|
|
||||||
asyncio_timer_vtable.start = asyncio_timer_start |
|
||||||
asyncio_timer_vtable.stop = asyncio_timer_stop |
|
||||||
|
|
||||||
asyncio_pollset_vtable.init = asyncio_init_loop |
|
||||||
asyncio_pollset_vtable.poll = asyncio_run_loop |
|
||||||
asyncio_pollset_vtable.kick = asyncio_kick_loop |
|
||||||
asyncio_pollset_vtable.shutdown = asyncio_destroy_loop |
|
||||||
|
|
||||||
grpc_custom_iomgr_init( |
|
||||||
&asyncio_socket_vtable, |
|
||||||
&asyncio_resolver_vtable, |
|
||||||
&asyncio_timer_vtable, |
|
||||||
&asyncio_pollset_vtable |
|
||||||
) |
|
@ -1,24 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
cdef class _AsyncioResolver: |
|
||||||
cdef: |
|
||||||
object _loop |
|
||||||
grpc_custom_resolver* _grpc_resolver |
|
||||||
object _task_resolve |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioResolver create(grpc_custom_resolver* grpc_resolver) |
|
||||||
|
|
||||||
cdef void resolve(self, const char* host, const char* port) |
|
@ -1,56 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
|
|
||||||
cdef class _AsyncioResolver: |
|
||||||
def __cinit__(self): |
|
||||||
self._loop = get_working_loop() |
|
||||||
self._grpc_resolver = NULL |
|
||||||
self._task_resolve = None |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioResolver create(grpc_custom_resolver* grpc_resolver): |
|
||||||
resolver = _AsyncioResolver() |
|
||||||
resolver._grpc_resolver = grpc_resolver |
|
||||||
return resolver |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
class_name = self.__class__.__name__ |
|
||||||
id_ = id(self) |
|
||||||
return f"<{class_name} {id_}>" |
|
||||||
|
|
||||||
async def _async_resolve(self, bytes host, bytes port): |
|
||||||
self._task_resolve = None |
|
||||||
try: |
|
||||||
resolved = await self._loop.getaddrinfo(host, port) |
|
||||||
except Exception as e: |
|
||||||
grpc_custom_resolve_callback( |
|
||||||
<grpc_custom_resolver*>self._grpc_resolver, |
|
||||||
NULL, |
|
||||||
grpc_socket_error("Resolve address [{}:{}] failed: {}: {}".format( |
|
||||||
host, port, type(e), str(e)).encode()) |
|
||||||
) |
|
||||||
else: |
|
||||||
grpc_custom_resolve_callback( |
|
||||||
<grpc_custom_resolver*>self._grpc_resolver, |
|
||||||
tuples_to_resolvaddr(resolved), |
|
||||||
<grpc_error_handle>0 |
|
||||||
) |
|
||||||
|
|
||||||
cdef void resolve(self, const char* host, const char* port): |
|
||||||
assert not self._task_resolve |
|
||||||
|
|
||||||
self._task_resolve = self._loop.create_task( |
|
||||||
self._async_resolve(host, port) |
|
||||||
) |
|
@ -1,63 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
|
|
||||||
cdef class _AsyncioSocket: |
|
||||||
cdef: |
|
||||||
# Common attributes |
|
||||||
grpc_custom_socket * _grpc_socket |
|
||||||
grpc_custom_read_callback _grpc_read_cb |
|
||||||
grpc_custom_write_callback _grpc_write_cb |
|
||||||
object _reader |
|
||||||
object _writer |
|
||||||
object _task_read |
|
||||||
object _task_write |
|
||||||
object _task_connect |
|
||||||
object _task_listen |
|
||||||
char * _read_buffer |
|
||||||
# Caches the picked event loop, so we can avoid the 30ns overhead each |
|
||||||
# time we need access to the event loop. |
|
||||||
object _loop |
|
||||||
# TODO(lidiz) Drop after 3.6 deprecation. Python 3.7 introduces methods |
|
||||||
# like `is_closing()` to help graceful shutdown. |
|
||||||
bint _closed |
|
||||||
|
|
||||||
# Client-side attributes |
|
||||||
grpc_custom_connect_callback _grpc_connect_cb |
|
||||||
|
|
||||||
# Server-side attributes |
|
||||||
grpc_custom_accept_callback _grpc_accept_cb |
|
||||||
grpc_custom_socket * _grpc_client_socket |
|
||||||
object _server |
|
||||||
object _py_socket |
|
||||||
object _peername |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioSocket create( |
|
||||||
grpc_custom_socket * grpc_socket, |
|
||||||
object reader, |
|
||||||
object writer) |
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioSocket create_with_py_socket(grpc_custom_socket * grpc_socket, object py_socket) |
|
||||||
|
|
||||||
cdef void connect(self, object host, object port, grpc_custom_connect_callback grpc_connect_cb) |
|
||||||
cdef void write(self, grpc_slice_buffer * g_slice_buffer, grpc_custom_write_callback grpc_write_cb) |
|
||||||
cdef void read(self, char * buffer_, size_t length, grpc_custom_read_callback grpc_read_cb) |
|
||||||
cdef bint is_connected(self) |
|
||||||
cdef void close(self) |
|
||||||
|
|
||||||
cdef accept(self, grpc_custom_socket* grpc_socket_client, grpc_custom_accept_callback grpc_accept_cb) |
|
||||||
cdef listen(self) |
|
||||||
cdef tuple peername(self) |
|
||||||
cdef tuple sockname(self) |
|
@ -1,225 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
import socket as native_socket |
|
||||||
|
|
||||||
from libc cimport string |
|
||||||
|
|
||||||
cdef int _ASYNCIO_STREAM_DEFAULT_SOCKET_BACKLOG = 100 |
|
||||||
|
|
||||||
|
|
||||||
# TODO(https://github.com/grpc/grpc/issues/21348) Better flow control needed. |
|
||||||
cdef class _AsyncioSocket: |
|
||||||
def __cinit__(self): |
|
||||||
self._grpc_socket = NULL |
|
||||||
self._grpc_connect_cb = NULL |
|
||||||
self._grpc_read_cb = NULL |
|
||||||
self._grpc_write_cb = NULL |
|
||||||
self._reader = None |
|
||||||
self._writer = None |
|
||||||
self._task_connect = None |
|
||||||
self._task_read = None |
|
||||||
self._task_write = None |
|
||||||
self._task_listen = None |
|
||||||
self._read_buffer = NULL |
|
||||||
self._server = None |
|
||||||
self._py_socket = None |
|
||||||
self._peername = None |
|
||||||
self._closed = False |
|
||||||
self._loop = get_working_loop() |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioSocket create(grpc_custom_socket * grpc_socket, |
|
||||||
object reader, |
|
||||||
object writer): |
|
||||||
socket = _AsyncioSocket() |
|
||||||
socket._grpc_socket = grpc_socket |
|
||||||
socket._reader = reader |
|
||||||
socket._writer = writer |
|
||||||
if writer is not None: |
|
||||||
socket._peername = writer.get_extra_info('peername') |
|
||||||
return socket |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioSocket create_with_py_socket(grpc_custom_socket * grpc_socket, object py_socket): |
|
||||||
socket = _AsyncioSocket() |
|
||||||
socket._grpc_socket = grpc_socket |
|
||||||
socket._py_socket = py_socket |
|
||||||
return socket |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
class_name = self.__class__.__name__ |
|
||||||
id_ = id(self) |
|
||||||
connected = self.is_connected() |
|
||||||
return f"<{class_name} {id_} connected={connected}>" |
|
||||||
|
|
||||||
async def _async_connect(self, object host, object port,): |
|
||||||
self._task_connect = None |
|
||||||
try: |
|
||||||
self._reader, self._writer = await asyncio.open_connection(host, port) |
|
||||||
except Exception as e: |
|
||||||
self._grpc_connect_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
grpc_socket_error("Socket connect failed: {}: {}".format(type(e), str(e)).encode()) |
|
||||||
) |
|
||||||
else: |
|
||||||
# gRPC default posix implementation disables nagle |
|
||||||
# algorithm. |
|
||||||
sock = self._writer.transport.get_extra_info('socket') |
|
||||||
sock.setsockopt(native_socket.IPPROTO_TCP, native_socket.TCP_NODELAY, True) |
|
||||||
|
|
||||||
self._grpc_connect_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
<grpc_error_handle>0 |
|
||||||
) |
|
||||||
|
|
||||||
cdef void connect(self, |
|
||||||
object host, |
|
||||||
object port, |
|
||||||
grpc_custom_connect_callback grpc_connect_cb): |
|
||||||
assert not self._reader |
|
||||||
assert not self._task_connect |
|
||||||
|
|
||||||
self._task_connect = self._loop.create_task( |
|
||||||
self._async_connect(host, port) |
|
||||||
) |
|
||||||
self._grpc_connect_cb = grpc_connect_cb |
|
||||||
|
|
||||||
async def _async_read(self, size_t length): |
|
||||||
self._task_read = None |
|
||||||
try: |
|
||||||
inbound_buffer = await self._reader.read(n=length) |
|
||||||
except ConnectionError as e: |
|
||||||
self._grpc_read_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
-1, |
|
||||||
grpc_socket_error("Read failed: {}".format(e).encode()) |
|
||||||
) |
|
||||||
else: |
|
||||||
string.memcpy( |
|
||||||
<void*>self._read_buffer, |
|
||||||
<char*>inbound_buffer, |
|
||||||
len(inbound_buffer) |
|
||||||
) |
|
||||||
self._grpc_read_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
len(inbound_buffer), |
|
||||||
<grpc_error_handle>0 |
|
||||||
) |
|
||||||
|
|
||||||
cdef void read(self, char * buffer_, size_t length, grpc_custom_read_callback grpc_read_cb): |
|
||||||
assert not self._task_read |
|
||||||
|
|
||||||
self._grpc_read_cb = grpc_read_cb |
|
||||||
self._read_buffer = buffer_ |
|
||||||
self._task_read = self._loop.create_task(self._async_read(length)) |
|
||||||
|
|
||||||
async def _async_write(self, bytearray outbound_buffer): |
|
||||||
self._writer.write(outbound_buffer) |
|
||||||
self._task_write = None |
|
||||||
try: |
|
||||||
await self._writer.drain() |
|
||||||
self._grpc_write_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
<grpc_error_handle>0 |
|
||||||
) |
|
||||||
except ConnectionError as connection_error: |
|
||||||
self._grpc_write_cb( |
|
||||||
<grpc_custom_socket*>self._grpc_socket, |
|
||||||
grpc_socket_error("Socket write failed: {}".format(connection_error).encode()), |
|
||||||
) |
|
||||||
|
|
||||||
cdef void write(self, grpc_slice_buffer * g_slice_buffer, grpc_custom_write_callback grpc_write_cb): |
|
||||||
"""Performs write to network socket in AsyncIO. |
|
||||||
|
|
||||||
For each socket, Core guarantees there'll be only one ongoing write. |
|
||||||
When the write is finished, we need to call grpc_write_cb to notify |
|
||||||
Core that the work is done. |
|
||||||
""" |
|
||||||
assert not self._task_write |
|
||||||
cdef char* start |
|
||||||
cdef bytearray outbound_buffer = bytearray() |
|
||||||
for i in range(g_slice_buffer.count): |
|
||||||
start = grpc_slice_buffer_start(g_slice_buffer, i) |
|
||||||
length = grpc_slice_buffer_length(g_slice_buffer, i) |
|
||||||
outbound_buffer.extend(<bytes>start[:length]) |
|
||||||
|
|
||||||
self._grpc_write_cb = grpc_write_cb |
|
||||||
self._task_write = self._loop.create_task(self._async_write(outbound_buffer)) |
|
||||||
|
|
||||||
cdef bint is_connected(self): |
|
||||||
return self._reader and not self._reader._transport.is_closing() |
|
||||||
|
|
||||||
cdef void close(self): |
|
||||||
if self._closed: |
|
||||||
return |
|
||||||
else: |
|
||||||
self._closed = True |
|
||||||
if self.is_connected(): |
|
||||||
self._writer.close() |
|
||||||
if self._task_listen and not self._task_listen.done(): |
|
||||||
self._task_listen.close() |
|
||||||
if self._server: |
|
||||||
self._server.close() |
|
||||||
# NOTE(lidiz) If the asyncio.Server is created from a Python socket, |
|
||||||
# the server.close() won't release the fd until the close() is called |
|
||||||
# for the Python socket. |
|
||||||
if self._py_socket: |
|
||||||
self._py_socket.close() |
|
||||||
|
|
||||||
def _new_connection_callback(self, object reader, object writer): |
|
||||||
# If the socket is closed, stop. |
|
||||||
if self._closed: |
|
||||||
return |
|
||||||
|
|
||||||
# Close the connection if server is not started yet. |
|
||||||
if self._grpc_accept_cb == NULL: |
|
||||||
writer.close() |
|
||||||
return |
|
||||||
|
|
||||||
client_socket = _AsyncioSocket.create( |
|
||||||
self._grpc_client_socket, |
|
||||||
reader, |
|
||||||
writer, |
|
||||||
) |
|
||||||
|
|
||||||
self._grpc_client_socket.impl = <void*>client_socket |
|
||||||
cpython.Py_INCREF(client_socket) # Py_DECREF in asyncio_socket_destroy |
|
||||||
# Accept callback expects to be called with: |
|
||||||
# * grpc_custom_socket: A grpc custom socket for server |
|
||||||
# * grpc_custom_socket: A grpc custom socket for client (with new Socket instance) |
|
||||||
# * grpc_error: An error object |
|
||||||
self._grpc_accept_cb(self._grpc_socket, self._grpc_client_socket, grpc_error_none()) |
|
||||||
|
|
||||||
cdef listen(self): |
|
||||||
self._py_socket.listen(_ASYNCIO_STREAM_DEFAULT_SOCKET_BACKLOG) |
|
||||||
async def create_asyncio_server(): |
|
||||||
self._server = await asyncio.start_server( |
|
||||||
self._new_connection_callback, |
|
||||||
sock=self._py_socket, |
|
||||||
) |
|
||||||
|
|
||||||
self._task_listen = self._loop.create_task(create_asyncio_server()) |
|
||||||
|
|
||||||
cdef accept(self, |
|
||||||
grpc_custom_socket* grpc_socket_client, |
|
||||||
grpc_custom_accept_callback grpc_accept_cb): |
|
||||||
self._grpc_client_socket = grpc_socket_client |
|
||||||
self._grpc_accept_cb = grpc_accept_cb |
|
||||||
|
|
||||||
cdef tuple peername(self): |
|
||||||
return self._peername |
|
||||||
|
|
||||||
cdef tuple sockname(self): |
|
||||||
return self._py_socket.getsockname() |
|
@ -1,25 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
cdef class _AsyncioTimer: |
|
||||||
cdef: |
|
||||||
grpc_custom_timer * _grpc_timer |
|
||||||
object _timer_future |
|
||||||
bint _active |
|
||||||
object _loop |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, float timeout) |
|
||||||
|
|
||||||
cdef stop(self) |
|
@ -1,48 +0,0 @@ |
|||||||
# Copyright 2019 gRPC authors. |
|
||||||
# |
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
# you may not use this file except in compliance with the License. |
|
||||||
# You may obtain a copy of the License at |
|
||||||
# |
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 |
|
||||||
# |
|
||||||
# Unless required by applicable law or agreed to in writing, software |
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
# See the License for the specific language governing permissions and |
|
||||||
# limitations under the License. |
|
||||||
|
|
||||||
|
|
||||||
cdef class _AsyncioTimer: |
|
||||||
def __cinit__(self): |
|
||||||
self._grpc_timer = NULL |
|
||||||
self._timer_future = None |
|
||||||
self._active = False |
|
||||||
self._loop = get_working_loop() |
|
||||||
cpython.Py_INCREF(self) |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
cdef _AsyncioTimer create(grpc_custom_timer * grpc_timer, float timeout): |
|
||||||
timer = _AsyncioTimer() |
|
||||||
timer._grpc_timer = grpc_timer |
|
||||||
timer._timer_future = timer._loop.call_later(timeout, timer.on_time_up) |
|
||||||
timer._active = True |
|
||||||
return timer |
|
||||||
|
|
||||||
def on_time_up(self): |
|
||||||
self._active = False |
|
||||||
grpc_custom_timer_callback(self._grpc_timer, <grpc_error_handle>0) |
|
||||||
cpython.Py_DECREF(self) |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
class_name = self.__class__.__name__ |
|
||||||
id_ = id(self) |
|
||||||
return f"<{class_name} {id_} deadline={self._deadline} active={self._active}>" |
|
||||||
|
|
||||||
cdef stop(self): |
|
||||||
if not self._active: |
|
||||||
return |
|
||||||
|
|
||||||
self._timer_future.cancel() |
|
||||||
self._active = False |
|
||||||
cpython.Py_DECREF(self) |
|
Loading…
Reference in new issue