diff -r 69c0321d644d Lib/asyncio/selector_events.py --- a/Lib/asyncio/selector_events.py Mon Mar 09 15:55:37 2015 +0100 +++ b/Lib/asyncio/selector_events.py Mon Mar 09 16:58:27 2015 +0100 @@ -408,14 +408,11 @@ class BaseSelectorEventLoop(base_events. def _sock_connect(self, fut, sock, address): fd = sock.fileno() try: - while True: - try: - sock.connect(address) - except InterruptedError: - continue - else: - break - except BlockingIOError: + sock.connect(address) + except (BlockingIOError, InterruptedError): + # Issue #23618: If connect() returns EWOULDBLOCK or EINTR, the + # connection runs asynchronously. Poll until the socket is writable + # to be notified when the connection completed. fut.add_done_callback(functools.partial(self._sock_connect_done, fd)) self.add_writer(fd, self._sock_connect_cb, fut, sock, address) diff -r 69c0321d644d Modules/socketmodule.c --- a/Modules/socketmodule.c Mon Mar 09 15:55:37 2015 +0100 +++ b/Modules/socketmodule.c Mon Mar 09 16:58:27 2015 +0100 @@ -2389,8 +2389,12 @@ internal_connect(PySocketSockObject *s, { int res, timeout; + assert(!PyErr_Occurred()); timeout = 0; + + Py_BEGIN_ALLOW_THREADS res = connect(s->sock_fd, addr, addrlen); + Py_END_ALLOW_THREADS #ifdef MS_WINDOWS @@ -2401,12 +2405,15 @@ internal_connect(PySocketSockObject *s, fd_set fds; fd_set fds_exc; struct timeval tv; + tv.tv_sec = (int)s->sock_timeout; tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); FD_ZERO(&fds_exc); FD_SET(s->sock_fd, &fds_exc); + + Py_BEGIN_ALLOW_THREADS res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), NULL, &fds, &fds_exc, &tv); if (res == 0) { @@ -2433,6 +2440,7 @@ internal_connect(PySocketSockObject *s, } } /* else if (res < 0) an error occurred */ + Py_END_ALLOW_THREADS } } @@ -2441,26 +2449,46 @@ internal_connect(PySocketSockObject *s, #else - if (s->sock_timeout > 0.0) { - if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) { - timeout = internal_select(s, 1); - if (timeout == 0) { - /* Bug #1019808: in case of an EINPROGRESS, - use getsockopt(SO_ERROR) to get the real - error. */ - socklen_t res_size = sizeof res; - (void)getsockopt(s->sock_fd, SOL_SOCKET, - SO_ERROR, &res, &res_size); - if (res == EISCONN) - res = 0; - errno = res; - } - else if (timeout == -1) { - res = errno; /* had error */ - } - else - res = EWOULDBLOCK; /* timed out */ + if (res < 0 && errno == EINTR) { + if (PyErr_CheckSignals()) { + *timeoutp = 0; + return EINTR; } + + /* Issue #23618: when connect() fails with EINTR, the connection is + * running asynchronously. + * + * If the socket is blocking, wait until the socket is writable using + * select()/poll(), and then get the result of the connection using + * getsockopt(SO_ERROR). + * + * If the socket is non-blocking, the caller is responsible to + * implement similar code (it's the case in asyncio for example). + */ + } + + if (res < 0 + && ((errno == EINPROGRESS && s->sock_timeout > 0.0) || errno == EINTR) + && IS_SELECTABLE(s)) { + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 1); + if (timeout == 0) { + /* Bug #1019808: in case of an EINPROGRESS, + use getsockopt(SO_ERROR) to get the real + error. */ + socklen_t res_size = sizeof res; + (void)getsockopt(s->sock_fd, SOL_SOCKET, + SO_ERROR, &res, &res_size); + if (res == EISCONN) + res = 0; + errno = res; + } + else if (timeout == -1) { + res = errno; /* had error */ + } + else + res = EWOULDBLOCK; /* timed out */ + Py_END_ALLOW_THREADS } if (res < 0) @@ -2485,9 +2513,9 @@ sock_connect(PySocketSockObject *s, PyOb if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen)) return NULL; - Py_BEGIN_ALLOW_THREADS res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout); - Py_END_ALLOW_THREADS + if (res == EINTR && PyErr_Occurred()) + return NULL; if (timeout == 1) { PyErr_SetString(socket_timeout, "timed out"); @@ -2519,13 +2547,8 @@ sock_connect_ex(PySocketSockObject *s, P if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen)) return NULL; - Py_BEGIN_ALLOW_THREADS res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout); - Py_END_ALLOW_THREADS - - /* Signals are not errors (though they may raise exceptions). Adapted - from PyErr_SetFromErrnoWithFilenameObject(). */ - if (res == EINTR && PyErr_CheckSignals()) + if (res == EINTR && PyErr_Occurred()) return NULL; return PyLong_FromLong((long) res);