summaryrefslogtreecommitdiffstats
path: root/Modules/socketmodule.c
diff options
context:
space:
mode:
authorCharles-François Natali <cf.natali@gmail.com>2015-02-07 13:27:50 (GMT)
committerCharles-François Natali <cf.natali@gmail.com>2015-02-07 13:27:50 (GMT)
commit6e6c59b5085668f6047eb91bd671747b13fa36d1 (patch)
tree8f1c506e399e201d40f8009a081039a071f875aa /Modules/socketmodule.c
parentd005090e01b46f00494f9f268b4c8dc2b28ffe2c (diff)
downloadcpython-6e6c59b5085668f6047eb91bd671747b13fa36d1.zip
cpython-6e6c59b5085668f6047eb91bd671747b13fa36d1.tar.gz
cpython-6e6c59b5085668f6047eb91bd671747b13fa36d1.tar.bz2
Issue #23285: PEP 475 -- Retry system calls failing with EINTR.
Diffstat (limited to 'Modules/socketmodule.c')
-rw-r--r--Modules/socketmodule.c239
1 files changed, 127 insertions, 112 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 94cf773..5913e65 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -2037,6 +2037,7 @@ sock_accept(PySocketSockObject *s)
PyObject *addr = NULL;
PyObject *res = NULL;
int timeout;
+ int async_err = 0;
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
static int accept4_works = -1;
@@ -2050,27 +2051,27 @@ sock_accept(PySocketSockObject *s)
return select_error();
BEGIN_SELECT_LOOP(s)
-
- Py_BEGIN_ALLOW_THREADS
- timeout = internal_select_ex(s, 0, interval);
- if (!timeout) {
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout) {
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- if (accept4_works != 0) {
- newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
- SOCK_CLOEXEC);
- if (newfd == INVALID_SOCKET && accept4_works == -1) {
- /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
- accept4_works = (errno != ENOSYS);
+ if (accept4_works != 0) {
+ newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
+ SOCK_CLOEXEC);
+ if (newfd == INVALID_SOCKET && accept4_works == -1) {
+ /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
+ accept4_works = (errno != ENOSYS);
+ }
}
- }
- if (accept4_works == 0)
- newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+ if (accept4_works == 0)
+ newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#else
- newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
+ newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#endif
- }
- Py_END_ALLOW_THREADS
-
+ }
+ Py_END_ALLOW_THREADS
+ } while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
return NULL;
@@ -2078,7 +2079,7 @@ sock_accept(PySocketSockObject *s)
END_SELECT_LOOP(s)
if (newfd == INVALID_SOCKET)
- return s->errorhandler();
+ return (!async_err) ? s->errorhandler() : NULL;
#ifdef MS_WINDOWS
if (!SetHandleInformation((HANDLE)newfd, HANDLE_FLAG_INHERIT, 0)) {
@@ -2341,6 +2342,10 @@ sock_close(PySocketSockObject *s)
{
SOCKET_T fd;
+ /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/
+ * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
+ * for more details.
+ */
if ((fd = s->sock_fd) != -1) {
s->sock_fd = -1;
Py_BEGIN_ALLOW_THREADS
@@ -2513,10 +2518,8 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
/* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */
-#ifdef EINTR
if (res == EINTR && PyErr_CheckSignals())
return NULL;
-#endif
return PyLong_FromLong((long) res);
}
@@ -2650,6 +2653,7 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
{
Py_ssize_t outlen = -1;
int timeout;
+ int async_err = 0;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2661,18 +2665,20 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
}
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS
- timeout = internal_select_ex(s, 0, interval);
- if (!timeout) {
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout) {
#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- outlen = recv(s->sock_fd, cbuf, (int)len, flags);
+ if (len > INT_MAX)
+ len = INT_MAX;
+ outlen = recv(s->sock_fd, cbuf, (int)len, flags);
#else
- outlen = recv(s->sock_fd, cbuf, len, flags);
+ outlen = recv(s->sock_fd, cbuf, len, flags);
#endif
- }
- Py_END_ALLOW_THREADS
+ }
+ Py_END_ALLOW_THREADS
+ } while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -2682,7 +2688,8 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
if (outlen < 0) {
/* Note: the call to errorhandler() ALWAYS indirectly returned
NULL, so ignore its return value */
- s->errorhandler();
+ if (!async_err)
+ s->errorhandler();
return -1;
}
return outlen;
@@ -2819,6 +2826,7 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
int timeout;
Py_ssize_t n = -1;
socklen_t addrlen;
+ int async_err = 0;
*addr = NULL;
@@ -2831,21 +2839,23 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
}
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS
- memset(&addrbuf, 0, addrlen);
- timeout = internal_select_ex(s, 0, interval);
- if (!timeout) {
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ memset(&addrbuf, 0, addrlen);
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout) {
#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
- (void *) &addrbuf, &addrlen);
+ if (len > INT_MAX)
+ len = INT_MAX;
+ n = recvfrom(s->sock_fd, cbuf, (int)len, flags,
+ (void *) &addrbuf, &addrlen);
#else
- n = recvfrom(s->sock_fd, cbuf, len, flags,
- SAS2SA(&addrbuf), &addrlen);
+ n = recvfrom(s->sock_fd, cbuf, len, flags,
+ SAS2SA(&addrbuf), &addrlen);
#endif
- }
- Py_END_ALLOW_THREADS
+ }
+ Py_END_ALLOW_THREADS
+ } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -2853,7 +2863,8 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
}
END_SELECT_LOOP(s)
if (n < 0) {
- s->errorhandler();
+ if (!async_err)
+ s->errorhandler();
return -1;
}
@@ -2993,6 +3004,7 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
{
ssize_t bytes_received = -1;
int timeout;
+ int async_err = 0;
sock_addr_t addrbuf;
socklen_t addrbuflen;
struct msghdr msg = {0};
@@ -3028,25 +3040,29 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
}
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS;
- msg.msg_name = SAS2SA(&addrbuf);
- msg.msg_namelen = addrbuflen;
- msg.msg_iov = iov;
- msg.msg_iovlen = iovlen;
- msg.msg_control = controlbuf;
- msg.msg_controllen = controllen;
- timeout = internal_select_ex(s, 0, interval);
- if (!timeout)
- bytes_received = recvmsg(s->sock_fd, &msg, flags);
- Py_END_ALLOW_THREADS;
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
- goto finally;
- }
+ do {
+ Py_BEGIN_ALLOW_THREADS;
+ msg.msg_name = SAS2SA(&addrbuf);
+ msg.msg_namelen = addrbuflen;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iovlen;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = controllen;
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout)
+ bytes_received = recvmsg(s->sock_fd, &msg, flags);
+ Py_END_ALLOW_THREADS;
+ if (timeout == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ goto finally;
+ }
+ } while (bytes_received < 0 && errno == EINTR &&
+ !(async_err = PyErr_CheckSignals()));
END_SELECT_LOOP(s)
if (bytes_received < 0) {
- s->errorhandler();
+ if (!async_err)
+ s->errorhandler();
goto finally;
}
@@ -3305,6 +3321,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
{
char *buf;
Py_ssize_t len, n = -1;
+ int async_err = 0;
int flags = 0, timeout;
Py_buffer pbuf;
@@ -3319,18 +3336,20 @@ sock_send(PySocketSockObject *s, PyObject *args)
len = pbuf.len;
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS
- timeout = internal_select_ex(s, 1, interval);
- if (!timeout) {
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ timeout = internal_select_ex(s, 1, interval);
+ if (!timeout) {
#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = send(s->sock_fd, buf, (int)len, flags);
+ if (len > INT_MAX)
+ len = INT_MAX;
+ n = send(s->sock_fd, buf, (int)len, flags);
#else
- n = send(s->sock_fd, buf, len, flags);
+ n = send(s->sock_fd, buf, len, flags);
#endif
- }
- Py_END_ALLOW_THREADS
+ }
+ Py_END_ALLOW_THREADS
+ } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) {
PyBuffer_Release(&pbuf);
PyErr_SetString(socket_timeout, "timed out");
@@ -3340,7 +3359,7 @@ sock_send(PySocketSockObject *s, PyObject *args)
PyBuffer_Release(&pbuf);
if (n < 0)
- return s->errorhandler();
+ return (!async_err) ? s->errorhandler() : NULL;
return PyLong_FromSsize_t(n);
}
@@ -3359,7 +3378,8 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
{
char *buf;
Py_ssize_t len, n = -1;
- int flags = 0, timeout, saved_errno;
+ int async_err = 0;
+ int flags = 0, timeout;
Py_buffer pbuf;
if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
@@ -3391,29 +3411,16 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
- /* PyErr_CheckSignals() might change errno */
- saved_errno = errno;
- /* We must run our signal handlers before looping again.
- send() can return a successful partial write when it is
- interrupted, so we can't restrict ourselves to EINTR. */
- if (PyErr_CheckSignals()) {
- PyBuffer_Release(&pbuf);
- return NULL;
- }
- if (n < 0) {
- /* If interrupted, try again */
- if (saved_errno == EINTR)
- continue;
- else
- break;
+ if (n >= 0) {
+ buf += n;
+ len -= n;
}
- buf += n;
- len -= n;
- } while (len > 0);
+ } while (len > 0 && (n >= 0 || errno == EINTR) &&
+ !(async_err = PyErr_CheckSignals()));
PyBuffer_Release(&pbuf);
- if (n < 0)
- return s->errorhandler();
+ if (n < 0 || async_err)
+ return (!async_err) ? s->errorhandler() : NULL;
Py_INCREF(Py_None);
return Py_None;
@@ -3439,6 +3446,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
Py_ssize_t len, arglen;
sock_addr_t addrbuf;
int addrlen, n = -1, flags, timeout;
+ int async_err = 0;
flags = 0;
arglen = PyTuple_Size(args);
@@ -3473,20 +3481,22 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
}
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS
- timeout = internal_select_ex(s, 1, interval);
- if (!timeout) {
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ timeout = internal_select_ex(s, 1, interval);
+ if (!timeout) {
#ifdef MS_WINDOWS
- if (len > INT_MAX)
- len = INT_MAX;
- n = sendto(s->sock_fd, buf, (int)len, flags,
- SAS2SA(&addrbuf), addrlen);
+ if (len > INT_MAX)
+ len = INT_MAX;
+ n = sendto(s->sock_fd, buf, (int)len, flags,
+ SAS2SA(&addrbuf), addrlen);
#else
- n = sendto(s->sock_fd, buf, len, flags,
- SAS2SA(&addrbuf), addrlen);
+ n = sendto(s->sock_fd, buf, len, flags,
+ SAS2SA(&addrbuf), addrlen);
#endif
- }
- Py_END_ALLOW_THREADS
+ }
+ Py_END_ALLOW_THREADS
+ } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) {
PyBuffer_Release(&pbuf);
@@ -3496,7 +3506,7 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
END_SELECT_LOOP(s)
PyBuffer_Release(&pbuf);
if (n < 0)
- return s->errorhandler();
+ return (!async_err) ? s->errorhandler() : NULL;
return PyLong_FromSsize_t(n);
}
@@ -3528,6 +3538,7 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
void *controlbuf = NULL;
size_t controllen, controllen_last;
ssize_t bytes_sent = -1;
+ int async_err = 0;
int addrlen, timeout, flags = 0;
PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
*cmsg_fast = NULL, *retval = NULL;
@@ -3685,19 +3696,23 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
}
BEGIN_SELECT_LOOP(s)
- Py_BEGIN_ALLOW_THREADS;
- timeout = internal_select_ex(s, 1, interval);
- if (!timeout)
- bytes_sent = sendmsg(s->sock_fd, &msg, flags);
- Py_END_ALLOW_THREADS;
- if (timeout == 1) {
- PyErr_SetString(socket_timeout, "timed out");
- goto finally;
- }
+ do {
+ Py_BEGIN_ALLOW_THREADS;
+ timeout = internal_select_ex(s, 1, interval);
+ if (!timeout)
+ bytes_sent = sendmsg(s->sock_fd, &msg, flags);
+ Py_END_ALLOW_THREADS;
+ if (timeout == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ goto finally;
+ }
+ } while (bytes_sent < 0 && errno == EINTR &&
+ !(async_err = PyErr_CheckSignals()));
END_SELECT_LOOP(s)
if (bytes_sent < 0) {
- s->errorhandler();
+ if (!async_err)
+ s->errorhandler();
goto finally;
}
retval = PyLong_FromSsize_t(bytes_sent);