Skip to content

Commit 35b0fab

Browse files
committed
start unifying uvloop & winloop together
1 parent a308f75 commit 35b0fab

File tree

11 files changed

+238
-76
lines changed

11 files changed

+238
-76
lines changed

uvloop/dns.pyx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
6161
addr6.sin6_scope_id
6262
)
6363

64-
elif addr.sa_family == uv.AF_UNIX:
64+
elif not system.PLATFORM_IS_WINDOWS and addr.sa_family == uv.AF_UNIX:
6565
addr_un = <system.sockaddr_un*>addr
6666
return system.MakeUnixSockPyAddr(addr_un)
6767

@@ -154,7 +154,7 @@ cdef __convert_pyaddr_to_sockaddr(int family, object addr,
154154
(<system.sockaddr_in6*>&ret.addr).sin6_flowinfo = flowinfo
155155
(<system.sockaddr_in6*>&ret.addr).sin6_scope_id = scope_id
156156

157-
elif family == uv.AF_UNIX:
157+
elif not system.PLATFORM_IS_WINDOWS and family == uv.AF_UNIX:
158158
if isinstance(addr, str):
159159
addr = addr.encode(sys_getfilesystemencoding())
160160
elif not isinstance(addr, bytes):
@@ -170,10 +170,14 @@ cdef __convert_pyaddr_to_sockaddr(int family, object addr,
170170
(<system.sockaddr_un*>&ret.addr).sun_family = uv.AF_UNIX
171171
memcpy((<system.sockaddr_un*>&ret.addr).sun_path, buf, buflen)
172172

173-
else:
173+
elif not system.PLATFORM_IS_WINDOWS:
174174
raise ValueError(
175175
f'expected AF_INET, AF_INET6, or AF_UNIX family, got {family}')
176176

177+
else:
178+
raise ValueError(
179+
f'expected AF_INET or AF_INET6 family, got {family}')
180+
177181
ret.family = family
178182
sockaddrs[addr] = ret
179183
memcpy(res, &ret.addr, ret.addr_size)

uvloop/errors.pyx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,23 @@ cdef __convert_python_error(int uverr):
88
# Implementation detail: on Unix error codes are the
99
# negated errno (or -errno), while on Windows they
1010
# are defined by libuv to arbitrary negative numbers.
11-
cdef int oserr = -uverr
11+
12+
cdef int oserr
13+
if system.PLATFORM_IS_WINDOWS:
14+
# XXX Won't work for Windows:
15+
# From libuv docs:
16+
# Implementation detail: on Unix error codes are the
17+
# negated errno (or -errno), while on Windows they
18+
# are defined by libuv to arbitrary negative numbers.
19+
20+
# Winloop comment: The following approach seems to work for Windows:
21+
# translation from uverr, which is a negative number like -4088 or -4071
22+
# defined by libuv (as mentioned above), to error numbers obtained via
23+
# the Python module errno.
24+
err = getattr(errno, uv.uv_err_name(uverr).decode(), uverr)
25+
return OSError(err, uv.uv_strerror(uverr).decode())
26+
27+
oserr = -uverr
1228

1329
exc = OSError
1430

uvloop/handles/poll.pyx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ cdef class UVPoll(UVHandle):
1010
self._abort_init()
1111
raise MemoryError()
1212

13-
err = uv.uv_poll_init(self._loop.uvloop,
13+
if system.PLATFORM_IS_WINDOWS:
14+
err = uv.uv_poll_init_socket(self._loop.uvloop,
15+
<uv.uv_poll_t *>self._handle, fd)
16+
else:
17+
err = uv.uv_poll_init(self._loop.uvloop,
1418
<uv.uv_poll_t *>self._handle, fd)
1519
if err < 0:
1620
self._abort_init()

uvloop/handles/process.pyx

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ cdef class UVProcess(UVHandle):
2828
pass_fds, debug_flags, preexec_fn, restore_signals):
2929

3030
global __forking
31-
global __forking_loop
32-
global __forkHandler
31+
32+
if not system.PLATFORM_IS_WINDOWS:
33+
global __forking_loop
34+
global __forkHandler
3335

3436
cdef int err
3537

@@ -89,22 +91,25 @@ cdef class UVProcess(UVHandle):
8991
self._restore_signals = restore_signals
9092

9193
loop.active_process_handler = self
92-
__forking = 1
93-
__forking_loop = loop
94-
system.setForkHandler(<system.OnForkHandler>&__get_fork_handler)
95-
96-
PyOS_BeforeFork()
94+
if not system.PLATFORM_IS_WINDOWS:
95+
__forking = 1
96+
__forking_loop = loop
97+
system.setForkHandler(<system.OnForkHandler>&__get_fork_handler)
9798

99+
PyOS_BeforeFork()
100+
98101
err = uv.uv_spawn(loop.uvloop,
99-
<uv.uv_process_t*>self._handle,
100-
&self.options)
101-
102-
__forking = 0
103-
__forking_loop = None
104-
system.resetForkHandler()
105-
loop.active_process_handler = None
106-
107-
PyOS_AfterFork_Parent()
102+
<uv.uv_process_t*>self._handle,
103+
&self.options)
104+
105+
if not system.PLATFORM_IS_WINDOWS:
106+
__forking = 0
107+
__forking_loop = None
108+
system.resetForkHandler()
109+
110+
PyOS_AfterFork_Parent()
111+
112+
108113

109114
if err < 0:
110115
self._close_process_handle()
@@ -178,11 +183,12 @@ cdef class UVProcess(UVHandle):
178183
if self._restore_signals:
179184
_Py_RestoreSignals()
180185

181-
PyOS_AfterFork_Child()
186+
if not system.PLATFORM_IS_WINDOWS:
187+
PyOS_AfterFork_Child()
182188

183-
err = uv.uv_loop_fork(self._loop.uvloop)
184-
if err < 0:
185-
raise convert_error(err)
189+
err = uv.uv_loop_fork(self._loop.uvloop)
190+
if err < 0:
191+
raise convert_error(err)
186192

187193
if self._preexec_fn is not None:
188194
try:
@@ -775,7 +781,15 @@ cdef __socketpair():
775781
int fds[2]
776782
int err
777783

778-
err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds)
784+
# Winloop comment: no Unix sockets on Windows, using uv.uv_pipe()
785+
# instead of system.socketpair(). Also, see changes to
786+
# libuv/src/win/pipe.c to deal with UV_EPERM = -4048 errors
787+
# for stdin pipe.
788+
if system.PLATFORM_IS_WINDOWS:
789+
# NB: uv.uv_file is int type on Windows
790+
err = uv.uv_pipe(fds, uv.UV_NONBLOCK_PIPE, uv.UV_NONBLOCK_PIPE)
791+
else:
792+
err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds)
779793
if err:
780794
exc = convert_error(-err)
781795
raise exc

uvloop/handles/stream.pyx

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,15 @@ cdef class UVStream(UVBaseTransport):
355355
Py_ssize_t blen
356356
int saved_errno
357357
int fd
358+
359+
if system.PLATFORM_IS_WINDOWS:
360+
# Winloop comment: WSASend below does not work with pipes.
361+
# For pipes, using Writefile() from Windows fileapi.h would
362+
# be an option, but the corresponding files have been created
363+
# FILE_FLAG_OVERLAPPED set, but we don't want to go that way here.
364+
# We detect pipes on Windows as pseudosockets.
365+
if self._get_socket().family == uv.AF_UNIX:
366+
return -1
358367

359368
if (<uv.uv_stream_t*>self._handle).write_queue_size != 0:
360369
raise RuntimeError(
@@ -383,16 +392,17 @@ cdef class UVStream(UVBaseTransport):
383392
# uv_try_write -- less layers of code. The error
384393
# checking logic is copied from libuv.
385394
written = system.write(fd, buf, blen)
386-
while written == -1 and (
387-
errno.errno == errno.EINTR or
388-
(system.PLATFORM_IS_APPLE and
389-
errno.errno == errno.EPROTOTYPE)):
390-
# From libuv code (unix/stream.c):
391-
# Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
392-
# EPROTOTYPE can be returned while trying to write to a socket
393-
# that is shutting down. If we retry the write, we should get
394-
# the expected EPIPE instead.
395-
written = system.write(fd, buf, blen)
395+
if not system.PLATFORM_IS_WINDOWS:
396+
while written == -1 and (
397+
errno.errno == errno.EINTR or
398+
(system.PLATFORM_IS_APPLE and
399+
errno.errno == errno.EPROTOTYPE)):
400+
# From libuv code (unix/stream.c):
401+
# Due to a possible kernel bug at least in OS X 10.10 "Yosemite",
402+
# EPROTOTYPE can be returned while trying to write to a socket
403+
# that is shutting down. If we retry the write, we should get
404+
# the expected EPIPE instead.
405+
written = system.write(fd, buf, blen)
396406
saved_errno = errno.errno
397407

398408
if used_buf:
@@ -675,6 +685,14 @@ cdef class UVStream(UVBaseTransport):
675685

676686
cpdef write(self, object buf):
677687
self._ensure_alive()
688+
689+
if system.PLATFORM_IS_WINDOWS:
690+
# Winloop Comment: Winloop gets itself into trouble if this is
691+
# is not checked immediately, it's too costly to call the python function
692+
# bring in the flag instead to indicate closure.
693+
# SEE: https://github.com/Vizonex/Winloop/issues/84
694+
if self._closing:
695+
raise RuntimeError("Cannot call write() when UVStream is closing")
678696

679697
if self._eof:
680698
raise RuntimeError('Cannot call write() after write_eof()')
@@ -806,7 +824,14 @@ cdef inline bint __uv_stream_on_read_common(
806824
if sc.__read_error_close:
807825
# Used for getting notified when a pipe is closed.
808826
# See WriteUnixTransport for the explanation.
809-
sc._on_eof()
827+
# Winloop comment: 0-reads on pipes used, e.g., for stdin
828+
# ("write only") give ERROR_ACCESS_DENIED, and in this case
829+
# we should keep the transport open for further writes.
830+
if (system.PLATFORM_IS_WINDOWS and nread == uv.UV_EPERM
831+
and uv.uv_is_writable(<uv.uv_stream_t*> sc._handle)):
832+
sc._stop_reading()
833+
else:
834+
sc._on_eof()
810835
return True
811836

812837
exc = convert_error(nread)

uvloop/includes/compat.h

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
#include <errno.h>
22
#include <stddef.h>
33
#include <signal.h>
4+
#ifndef _WIN32
45
#include <sys/socket.h>
56
#include <sys/un.h>
7+
#include <arpa/inet.h>
8+
#include <unistd.h>
9+
#else
10+
#include <io.h>
11+
#include <winsock2.h>
12+
#endif
13+
614
#include "Python.h"
715
#include "uv.h"
816

@@ -24,16 +32,49 @@
2432
#else
2533
# define PLATFORM_IS_LINUX 0
2634
# define EPOLL_CTL_DEL 2
27-
struct epoll_event {};
35+
/* error C2016: C requires that a struct or union have at least one member on Windows
36+
with default compilation flags. Therefore put dummy field for now. */
37+
struct epoll_event {int dummyfield;};
2838
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
2939
return 0;
3040
};
3141
#endif
3242

3343

44+
#ifdef _WIN32
45+
int SIGCHLD = 0;
46+
int SO_REUSEPORT = 0;
47+
48+
struct sockaddr_un {unsigned short sun_family; char* sun_path;};
49+
50+
int socketpair(int domain, int type, int protocol, int socket_vector[2]) {
51+
return 0;
52+
}
53+
54+
/* redefine write as counterpart of unistd.h/write */
55+
int write(int fd, const void *buf, unsigned int count) {
56+
WSABUF wsa;
57+
unsigned long dbytes;
58+
wsa.buf = (char*)buf;
59+
wsa.len = (unsigned long)count;
60+
errno = WSASend(fd, &wsa, 1, &dbytes, 0, NULL, NULL);
61+
if (errno == SOCKET_ERROR) {
62+
errno = WSAGetLastError();
63+
if (errno == 10035)
64+
errno = EAGAIN;
65+
return -1;
66+
}
67+
else
68+
return dbytes;
69+
}
70+
#endif
71+
3472
PyObject *
3573
MakeUnixSockPyAddr(struct sockaddr_un *addr)
3674
{
75+
#ifdef _WIN32
76+
return NULL;
77+
#else
3778
if (addr->sun_family != AF_UNIX) {
3879
PyErr_SetString(
3980
PyExc_ValueError, "a UNIX socket addr was expected");
@@ -52,8 +93,18 @@ MakeUnixSockPyAddr(struct sockaddr_un *addr)
5293
/* regular NULL-terminated string */
5394
return PyUnicode_DecodeFSDefault(addr->sun_path);
5495
}
96+
#endif /* _WIN32 */
5597
}
5698

99+
#ifdef _WIN32
100+
#define PLATFORM_IS_WINDOWS 1
101+
int getuid() {
102+
return 0;
103+
}
104+
#else
105+
#define PLATFORM_IS_WINDOWS 0
106+
#endif
107+
57108

58109
#if PY_VERSION_HEX < 0x03070100
59110

uvloop/includes/fork_handler.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#ifndef UVLOOP_FORK_HANDLER_H_
22
#define UVLOOP_FORK_HANDLER_H_
33

4+
#ifndef _WIN32
5+
#include <pthread.h>
6+
#endif
7+
48
volatile uint64_t MAIN_THREAD_ID = 0;
59
volatile int8_t MAIN_THREAD_ID_SET = 0;
610

@@ -39,4 +43,14 @@ void setMainThreadID(uint64_t id) {
3943
MAIN_THREAD_ID = id;
4044
MAIN_THREAD_ID_SET = 1;
4145
}
46+
47+
#ifdef _WIN32
48+
int pthread_atfork(
49+
void (*prepare)(),
50+
void (*parent)(),
51+
void (*child)()) {
52+
return 0;
53+
}
54+
#endif
55+
4256
#endif

uvloop/includes/stdlib.pxi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ cdef int subprocess_STDOUT = subprocess.STDOUT
146146
cdef int subprocess_DEVNULL = subprocess.DEVNULL
147147
cdef subprocess_SubprocessError = subprocess.SubprocessError
148148

149+
cdef int signal_SIGABRT = signal.SIGABRT
150+
cdef int signal_SIGINT = signal.SIGINT
149151
cdef int signal_NSIG = signal.NSIG
150152
cdef signal_signal = signal.signal
151153
cdef signal_siginterrupt = signal.siginterrupt

0 commit comments

Comments
 (0)