Skip to content

Commit 6a27cbe

Browse files
authored
Replace some SSL vectorcall with direct methods (#626)
1 parent 1d9b6e0 commit 6a27cbe

File tree

4 files changed

+118
-62
lines changed

4 files changed

+118
-62
lines changed

uvloop/handles/stream.pxd

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
cdef enum ProtocolType:
2+
SIMPLE = 0 # User Protocol doesn't support asyncio.BufferedProtocol
3+
BUFFERED = 1 # User Protocol supports asyncio.BufferedProtocol
4+
SSL_PROTOCOL = 2 # Our own SSLProtocol
5+
6+
17
cdef class UVStream(UVBaseTransport):
28
cdef:
39
uv.uv_shutdown_t _shutdown_req
410
bint __shutting_down
511
bint __reading
612
bint __read_error_close
713

8-
bint __buffered
14+
ProtocolType __protocol_type
915
object _protocol_get_buffer
1016
object _protocol_buffer_updated
1117

@@ -16,6 +22,8 @@ cdef class UVStream(UVBaseTransport):
1622
Py_buffer _read_pybuf
1723
bint _read_pybuf_acquired
1824

25+
cpdef write(self, object buf)
26+
1927
# All "inline" methods are final
2028

2129
cdef inline _init(self, Loop loop, object protocol, Server server,

uvloop/handles/stream.pyx

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,15 @@ cdef class UVStream(UVBaseTransport):
213213
self.__shutting_down = 0
214214
self.__reading = 0
215215
self.__read_error_close = 0
216-
self.__buffered = 0
217-
self._eof = 0
218-
self._buffer = []
219-
self._buffer_size = 0
220216

217+
self.__protocol_type = ProtocolType.SIMPLE
221218
self._protocol_get_buffer = None
222219
self._protocol_buffer_updated = None
223220

221+
self._eof = 0
222+
self._buffer = []
223+
self._buffer_size = 0
224+
224225
self._read_pybuf_acquired = False
225226

226227
cdef _set_protocol(self, object protocol):
@@ -229,22 +230,24 @@ cdef class UVStream(UVBaseTransport):
229230

230231
UVBaseTransport._set_protocol(self, protocol)
231232

232-
if (hasattr(protocol, 'get_buffer') and
233+
if isinstance(protocol, SSLProtocol):
234+
self.__protocol_type = ProtocolType.SSL_PROTOCOL
235+
elif (hasattr(protocol, 'get_buffer') and
233236
not isinstance(protocol, aio_Protocol)):
234237
try:
235238
self._protocol_get_buffer = protocol.get_buffer
236239
self._protocol_buffer_updated = protocol.buffer_updated
237-
self.__buffered = 1
240+
self.__protocol_type = ProtocolType.BUFFERED
238241
except AttributeError:
239242
pass
240243
else:
241-
self.__buffered = 0
244+
self.__protocol_type = ProtocolType.SIMPLE
242245

243246
cdef _clear_protocol(self):
244247
UVBaseTransport._clear_protocol(self)
245248
self._protocol_get_buffer = None
246249
self._protocol_buffer_updated = None
247-
self.__buffered = 0
250+
self.__protocol_type = ProtocolType.SIMPLE
248251

249252
cdef inline _shutdown(self):
250253
cdef int err
@@ -294,14 +297,14 @@ cdef class UVStream(UVBaseTransport):
294297
if self.__reading:
295298
return
296299

297-
if self.__buffered:
298-
err = uv.uv_read_start(<uv.uv_stream_t*>self._handle,
299-
__uv_stream_buffered_alloc,
300-
__uv_stream_buffered_on_read)
301-
else:
300+
if self.__protocol_type == ProtocolType.SIMPLE:
302301
err = uv.uv_read_start(<uv.uv_stream_t*>self._handle,
303302
__loop_alloc_buffer,
304303
__uv_stream_on_read)
304+
else:
305+
err = uv.uv_read_start(<uv.uv_stream_t *> self._handle,
306+
__uv_stream_buffered_alloc,
307+
__uv_stream_buffered_on_read)
305308
if err < 0:
306309
exc = convert_error(err)
307310
self._fatal_error(exc, True)
@@ -670,7 +673,7 @@ cdef class UVStream(UVBaseTransport):
670673
self.__reading,
671674
id(self))
672675

673-
def write(self, object buf):
676+
cpdef write(self, object buf):
674677
self._ensure_alive()
675678

676679
if self._eof:
@@ -920,9 +923,24 @@ cdef void __uv_stream_buffered_alloc(
920923
"UVStream alloc buffer callback") == 0:
921924
return
922925

926+
cdef UVStream sc = <UVStream>stream.data
927+
928+
# Fast pass for our own SSLProtocol
929+
# avoid python calls, memoryviews, context enter/exit, etc
930+
if sc.__protocol_type == ProtocolType.SSL_PROTOCOL:
931+
try:
932+
(<SSLProtocol>sc._protocol).get_buffer_impl(
933+
suggested_size, &uvbuf.base, &uvbuf.len)
934+
return
935+
except BaseException as exc:
936+
# Can't call 'sc._fatal_error' or 'sc._close', libuv will SF.
937+
# We'll do it later in __uv_stream_buffered_on_read when we
938+
# receive UV_ENOBUFS.
939+
uvbuf.len = 0
940+
uvbuf.base = NULL
941+
return
942+
923943
cdef:
924-
UVStream sc = <UVStream>stream.data
925-
Loop loop = sc._loop
926944
Py_buffer* pybuf = &sc._read_pybuf
927945
int got_buf = 0
928946

@@ -983,7 +1001,12 @@ cdef void __uv_stream_buffered_on_read(
9831001
return
9841002

9851003
try:
986-
if nread > 0 and not sc._read_pybuf_acquired:
1004+
# When our own SSLProtocol is used, we get buffer pointer directly,
1005+
# through SSLProtocol.get_buffer_impl, not through Py_Buffer interface.
1006+
# Therefore sc._read_pybuf_acquired is always False for SSLProtocol.
1007+
if (nread > 0 and
1008+
sc.__protocol_type == ProtocolType.BUFFERED and
1009+
not sc._read_pybuf_acquired):
9871010
# From libuv docs:
9881011
# nread is > 0 if there is data available or < 0 on error. When
9891012
# we’ve reached EOF, nread will be set to UV_EOF. When
@@ -1004,12 +1027,20 @@ cdef void __uv_stream_buffered_on_read(
10041027
if UVLOOP_DEBUG:
10051028
loop._debug_stream_read_cb_total += 1
10061029

1007-
run_in_context1(sc.context, sc._protocol_buffer_updated, nread)
1030+
if sc.__protocol_type == ProtocolType.SSL_PROTOCOL:
1031+
Context_Enter(sc.context)
1032+
try:
1033+
(<SSLProtocol>sc._protocol).buffer_updated_impl(nread)
1034+
finally:
1035+
Context_Exit(sc.context)
1036+
else:
1037+
run_in_context1(sc.context, sc._protocol_buffer_updated, nread)
10081038
except BaseException as exc:
10091039
if UVLOOP_DEBUG:
10101040
loop._debug_stream_read_cb_errors_total += 1
10111041

10121042
sc._fatal_error(exc, False)
10131043
finally:
1014-
sc._read_pybuf_acquired = 0
1015-
PyBuffer_Release(pybuf)
1044+
if sc._read_pybuf_acquired:
1045+
sc._read_pybuf_acquired = 0
1046+
PyBuffer_Release(pybuf)

uvloop/sslproto.pxd

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ cdef class SSLProtocol:
5959
object _outgoing_read
6060
char* _ssl_buffer
6161
size_t _ssl_buffer_len
62-
object _ssl_buffer_view
6362
SSLProtocolState _state
6463
size_t _conn_lost
6564
AppProtocolState _app_state
@@ -84,55 +83,61 @@ cdef class SSLProtocol:
8483
object _handshake_timeout_handle
8584
object _shutdown_timeout_handle
8685

87-
cdef _set_app_protocol(self, app_protocol)
88-
cdef _wakeup_waiter(self, exc=*)
89-
cdef _get_extra_info(self, name, default=*)
90-
cdef _set_state(self, SSLProtocolState new_state)
86+
# Instead of doing python calls, c methods *_impl are called directly
87+
# from stream.pyx
88+
89+
cdef inline get_buffer_impl(self, size_t n, char** buf, size_t* buf_size)
90+
cdef inline buffer_updated_impl(self, size_t nbytes)
91+
92+
cdef inline _set_app_protocol(self, app_protocol)
93+
cdef inline _wakeup_waiter(self, exc=*)
94+
cdef inline _get_extra_info(self, name, default=*)
95+
cdef inline _set_state(self, SSLProtocolState new_state)
9196

9297
# Handshake flow
9398

94-
cdef _start_handshake(self)
95-
cdef _check_handshake_timeout(self)
96-
cdef _do_handshake(self)
97-
cdef _on_handshake_complete(self, handshake_exc)
99+
cdef inline _start_handshake(self)
100+
cdef inline _check_handshake_timeout(self)
101+
cdef inline _do_handshake(self)
102+
cdef inline _on_handshake_complete(self, handshake_exc)
98103

99104
# Shutdown flow
100105

101-
cdef _start_shutdown(self, object context=*)
102-
cdef _check_shutdown_timeout(self)
103-
cdef _do_read_into_void(self, object context)
104-
cdef _do_flush(self, object context=*)
105-
cdef _do_shutdown(self, object context=*)
106-
cdef _on_shutdown_complete(self, shutdown_exc)
107-
cdef _abort(self, exc)
106+
cdef inline _start_shutdown(self, object context=*)
107+
cdef inline _check_shutdown_timeout(self)
108+
cdef inline _do_read_into_void(self, object context)
109+
cdef inline _do_flush(self, object context=*)
110+
cdef inline _do_shutdown(self, object context=*)
111+
cdef inline _on_shutdown_complete(self, shutdown_exc)
112+
cdef inline _abort(self, exc)
108113

109114
# Outgoing flow
110115

111-
cdef _write_appdata(self, list_of_data, object context)
112-
cdef _do_write(self)
113-
cdef _process_outgoing(self)
116+
cdef inline _write_appdata(self, list_of_data, object context)
117+
cdef inline _do_write(self)
118+
cdef inline _process_outgoing(self)
114119

115120
# Incoming flow
116121

117-
cdef _do_read(self)
118-
cdef _do_read__buffered(self)
119-
cdef _do_read__copied(self)
120-
cdef _call_eof_received(self, object context=*)
122+
cdef inline _do_read(self)
123+
cdef inline _do_read__buffered(self)
124+
cdef inline _do_read__copied(self)
125+
cdef inline _call_eof_received(self, object context=*)
121126

122127
# Flow control for writes from APP socket
123128

124-
cdef _control_app_writing(self, object context=*)
125-
cdef size_t _get_write_buffer_size(self)
126-
cdef _set_write_buffer_limits(self, high=*, low=*)
129+
cdef inline _control_app_writing(self, object context=*)
130+
cdef inline size_t _get_write_buffer_size(self)
131+
cdef inline _set_write_buffer_limits(self, high=*, low=*)
127132

128133
# Flow control for reads to APP socket
129134

130-
cdef _pause_reading(self)
131-
cdef _resume_reading(self, object context)
135+
cdef inline _pause_reading(self)
136+
cdef inline _resume_reading(self, object context)
132137

133138
# Flow control for reads from SSL socket
134139

135-
cdef _control_ssl_reading(self)
136-
cdef _set_read_buffer_limits(self, high=*, low=*)
137-
cdef size_t _get_read_buffer_size(self)
138-
cdef _fatal_error(self, exc, message=*)
140+
cdef inline _control_ssl_reading(self)
141+
cdef inline _set_read_buffer_limits(self, high=*, low=*)
142+
cdef inline size_t _get_read_buffer_size(self)
143+
cdef inline _fatal_error(self, exc, message=*)

uvloop/sslproto.pyx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,8 @@ cdef class SSLProtocol:
204204
self._ssl_buffer = <char*>PyMem_RawMalloc(self._ssl_buffer_len)
205205
if not self._ssl_buffer:
206206
raise MemoryError()
207-
self._ssl_buffer_view = PyMemoryView_FromMemory(
208-
self._ssl_buffer, self._ssl_buffer_len, PyBUF_WRITE)
209207

210208
def __dealloc__(self):
211-
self._ssl_buffer_view = None
212209
PyMem_RawFree(self._ssl_buffer)
213210
self._ssl_buffer = NULL
214211
self._ssl_buffer_len = 0
@@ -358,7 +355,7 @@ cdef class SSLProtocol:
358355
self._handshake_timeout_handle.cancel()
359356
self._handshake_timeout_handle = None
360357

361-
def get_buffer(self, n):
358+
cdef get_buffer_impl(self, size_t n, char** buf, size_t* buf_size):
362359
cdef size_t want = n
363360
if want > SSL_READ_MAX_SIZE:
364361
want = SSL_READ_MAX_SIZE
@@ -367,11 +364,11 @@ cdef class SSLProtocol:
367364
if not self._ssl_buffer:
368365
raise MemoryError()
369366
self._ssl_buffer_len = want
370-
self._ssl_buffer_view = PyMemoryView_FromMemory(
371-
self._ssl_buffer, want, PyBUF_WRITE)
372-
return self._ssl_buffer_view
373367

374-
def buffer_updated(self, nbytes):
368+
buf[0] = self._ssl_buffer
369+
buf_size[0] = self._ssl_buffer_len
370+
371+
cdef buffer_updated_impl(self, size_t nbytes):
375372
self._incoming_write(PyMemoryView_FromMemory(
376373
self._ssl_buffer, nbytes, PyBUF_WRITE))
377374

@@ -387,6 +384,18 @@ cdef class SSLProtocol:
387384
elif self._state == SHUTDOWN:
388385
self._do_shutdown()
389386

387+
def get_buffer(self, size_t n):
388+
# This pure python call is still used by some very peculiar test cases
389+
cdef:
390+
char* buf
391+
size_t buf_size
392+
393+
self.get_buffer_impl(n, &buf, &buf_size)
394+
return PyMemoryView_FromMemory(buf, buf_size, PyBUF_WRITE)
395+
396+
def buffer_updated(self, size_t nbytes):
397+
self.buffer_updated_impl(nbytes)
398+
390399
def eof_received(self):
391400
"""Called when the other end of the low-level stream
392401
is half-closed.
@@ -696,7 +705,10 @@ cdef class SSLProtocol:
696705
if not self._ssl_writing_paused:
697706
data = self._outgoing_read()
698707
if len(data):
699-
self._transport.write(data)
708+
if isinstance(self._transport, UVStream):
709+
(<UVStream>self._transport).write(data)
710+
else:
711+
self._transport.write(data)
700712

701713
# Incoming flow
702714

0 commit comments

Comments
 (0)