Skip to content

Commit dc8aefb

Browse files
committed
Speedup transport.write by using unistd.h/write directly
1 parent 19e95e2 commit dc8aefb

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

uvloop/handles/stream.pyx

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,43 +179,62 @@ cdef class UVStream(UVBaseTransport):
179179

180180
cdef inline _try_write(self, object data):
181181
cdef:
182-
int written
182+
ssize_t written
183183
bint used_buf = 0
184184
Py_buffer py_buf
185-
uv.uv_buf_t uv_buf
185+
void* buf
186+
size_t blen
187+
int saved_errno
188+
int fd
186189

187190
if PyBytes_CheckExact(data):
188-
uv_buf.base = PyBytes_AS_STRING(data)
189-
uv_buf.len = Py_SIZE(data)
191+
buf = <void*>PyBytes_AS_STRING(data)
192+
blen = Py_SIZE(data)
190193
elif PyByteArray_CheckExact(data):
191-
uv_buf.base = PyByteArray_AS_STRING(data)
192-
uv_buf.len = Py_SIZE(data)
194+
buf = <void*>PyByteArray_AS_STRING(data)
195+
blen = Py_SIZE(data)
193196
else:
194197
PyObject_GetBuffer(data, &py_buf, PyBUF_SIMPLE)
195198
used_buf = 1
196-
uv_buf.base = <char*>py_buf.buf
197-
uv_buf.len = py_buf.len
199+
buf = py_buf.buf
200+
blen = py_buf.len
198201

199-
written = uv.uv_try_write(
200-
<uv.uv_stream_t*>self._handle,
201-
&uv_buf, 1)
202+
if blen == 0:
203+
# Empty data, do nothing.
204+
return 0
205+
206+
fd = self._fileno()
207+
# Use `unistd.h/write` directly, it's faster than
208+
# uv_try_write -- less layers of code. The error
209+
# checking logic is copied from libuv.
210+
written = system.write(fd, buf, blen)
211+
while written == -1 and (errno.errno == errno.EINTR or
212+
(system.PLATFORM_IS_APPLE and
213+
errno.errno == errno.EPROTOTYPE)):
214+
# From libuv code (unix/stream.c):
215+
# Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
216+
# EPROTOTYPE can be returned while trying to write to a socket
217+
# that is shutting down. If we retry the write, we should get
218+
# the expected EPIPE instead.
219+
written = system.write(fd, buf, blen)
220+
saved_errno = errno.errno
202221

203222
if used_buf:
204223
PyBuffer_Release(&py_buf)
205224

206225
if written < 0:
207-
if written == uv.UV_EAGAIN:
226+
if saved_errno == errno.EAGAIN or \
227+
saved_errno == system.EWOULDBLOCK:
208228
return -1
209229
else:
210-
exc = convert_error(written)
230+
exc = convert_error(-saved_errno)
211231
self._fatal_error(exc, True)
212232
return
213233

214234
IF DEBUG:
215-
if written > 0:
216-
self._loop._debug_stream_write_tries += 1
235+
self._loop._debug_stream_write_tries += 1
217236

218-
if written == <int>uv_buf.len:
237+
if <size_t>written == blen:
219238
return 0
220239

221240
return written
@@ -229,6 +248,11 @@ cdef class UVStream(UVBaseTransport):
229248
# Try to write without polling only when there is
230249
# no data in write buffers.
231250
sent = self._try_write(data)
251+
if sent is None:
252+
# A `self._fatal_error` was called.
253+
# It might not raise an exception under some
254+
# conditions.
255+
return
232256
if sent == 0:
233257
# All data was successfully written.
234258
return

uvloop/includes/compat.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1+
#include <errno.h>
12
#include "uv.h"
23

34

45
// uv_poll_event.UV_DISCONNECT is available since libuv v1.9.0
56
#if UV_VERSION_HEX < 0x10900
67
#define UV_DISCONNECT 0
78
#endif
9+
10+
#ifndef EWOULDBLOCK
11+
#define EWOULDBLOCK EAGAIN
12+
#endif
13+
14+
#ifdef __APPLE__
15+
#define PLATFORM_IS_APPLE 1
16+
#else
17+
#define PLATFORM_IS_APPLE 0
18+
#endif

uvloop/includes/system.pxd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,14 @@ cdef extern from "sys/socket.h" nogil:
4343

4444
int setsockopt(int socket, int level, int option_name,
4545
const void *option_value, int option_len)
46+
47+
48+
cdef extern from "unistd.h" nogil:
49+
50+
ssize_t write(int fd, const void *buf, size_t count)
51+
52+
53+
cdef extern from "includes/compat.h" nogil:
54+
55+
cdef int EWOULDBLOCK
56+
cdef int PLATFORM_IS_APPLE

0 commit comments

Comments
 (0)