Issue #23618, #22117: refactor socketmodule.c

Move Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS inside internal_select_ex() to
prepare a switch to the _PyTime_t type and retry syscall on EINTR.
This commit is contained in:
Victor Stinner 2015-03-27 22:59:32 +01:00
parent 34dc0f46ae
commit b7df3144ef
1 changed files with 113 additions and 83 deletions

View File

@ -601,6 +601,11 @@ internal_select_ex(PySocketSockObject *s, int writing, double interval)
{ {
int n; int n;
#ifdef WITH_THREAD
/* must be called with the GIL held */
assert(PyGILState_Check());
#endif
/* Nothing to do unless we're in timeout mode (not non-blocking) */ /* Nothing to do unless we're in timeout mode (not non-blocking) */
if (s->sock_timeout <= 0.0) if (s->sock_timeout <= 0.0)
return 0; return 0;
@ -625,7 +630,10 @@ internal_select_ex(PySocketSockObject *s, int writing, double interval)
/* s->sock_timeout is in seconds, timeout in ms */ /* s->sock_timeout is in seconds, timeout in ms */
timeout = (int)(interval * 1000 + 0.5); timeout = (int)(interval * 1000 + 0.5);
Py_BEGIN_ALLOW_THREADS;
n = poll(&pollfd, 1, timeout); n = poll(&pollfd, 1, timeout);
Py_END_ALLOW_THREADS;
} }
#else #else
{ {
@ -638,12 +646,14 @@ internal_select_ex(PySocketSockObject *s, int writing, double interval)
FD_SET(s->sock_fd, &fds); FD_SET(s->sock_fd, &fds);
/* See if the socket is ready */ /* See if the socket is ready */
Py_BEGIN_ALLOW_THREADS;
if (writing) if (writing)
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, NULL, &tv); NULL, &fds, NULL, &tv);
else else
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
&fds, NULL, NULL, &tv); &fds, NULL, NULL, &tv);
Py_END_ALLOW_THREADS;
} }
#endif #endif
@ -667,11 +677,14 @@ internal_select(PySocketSockObject *s, int writing)
Here is an example of use: Here is an example of use:
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout)
if (!timeout) {
Py_BEGIN_ALLOW_THREADS
outlen = recv(s->sock_fd, cbuf, len, flags); outlen = recv(s->sock_fd, cbuf, len, flags);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
return -1; return -1;
@ -2066,9 +2079,10 @@ sock_accept(PySocketSockObject *s)
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if (accept4_works != 0) { if (accept4_works != 0) {
newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen,
@ -2083,8 +2097,8 @@ sock_accept(PySocketSockObject *s)
#else #else
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
} while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } while (newfd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -2395,18 +2409,25 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
{ {
int res, timeout; int res, timeout;
timeout = 0; timeout = 0;
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen); res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (s->sock_timeout > 0.0) { if (s->sock_timeout > 0.0
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK && && res < 0 && WSAGetLastError() == WSAEWOULDBLOCK
IS_SELECTABLE(s)) { && IS_SELECTABLE(s)) {
/* This is a mess. Best solution: trust select */ /* This is a mess. Best solution: trust select */
fd_set fds; fd_set fds;
fd_set fds_exc; fd_set fds_exc;
struct timeval tv; struct timeval tv;
Py_BEGIN_ALLOW_THREADS
tv.tv_sec = (int)s->sock_timeout; tv.tv_sec = (int)s->sock_timeout;
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
FD_ZERO(&fds); FD_ZERO(&fds);
@ -2415,6 +2436,8 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
FD_SET(s->sock_fd, &fds_exc); FD_SET(s->sock_fd, &fds_exc);
res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, &fds_exc, &tv); NULL, &fds, &fds_exc, &tv);
Py_END_ALLOW_THREADS
if (res == 0) { if (res == 0) {
res = WSAEWOULDBLOCK; res = WSAEWOULDBLOCK;
timeout = 1; timeout = 1;
@ -2440,16 +2463,17 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
} }
/* else if (res < 0) an error occurred */ /* else if (res < 0) an error occurred */
} }
}
if (res < 0) if (res < 0)
res = WSAGetLastError(); res = WSAGetLastError();
#else #else
if (s->sock_timeout > 0.0) { if (s->sock_timeout > 0.0
if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) { && res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
timeout = internal_select(s, 1); timeout = internal_select(s, 1);
if (timeout == 0) { if (timeout == 0) {
/* Bug #1019808: in case of an EINPROGRESS, /* Bug #1019808: in case of an EINPROGRESS,
use getsockopt(SO_ERROR) to get the real use getsockopt(SO_ERROR) to get the real
@ -2467,7 +2491,6 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
else else
res = EWOULDBLOCK; /* timed out */ res = EWOULDBLOCK; /* timed out */
} }
}
if (res < 0) if (res < 0)
res = errno; res = errno;
@ -2491,9 +2514,7 @@ sock_connect(PySocketSockObject *s, PyObject *addro)
if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen)) if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout); res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout);
Py_END_ALLOW_THREADS
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -2525,9 +2546,7 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen)) if (!getsockaddrarg(s, addro, SAS2SA(&addrbuf), &addrlen))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout); res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout);
Py_END_ALLOW_THREADS
/* Signals are not errors (though they may raise exceptions). Adapted /* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */ from PyErr_SetFromErrnoWithFilenameObject(). */
@ -2679,9 +2698,10 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
@ -2689,8 +2709,8 @@ sock_recv_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags)
#else #else
outlen = recv(s->sock_fd, cbuf, len, flags); outlen = recv(s->sock_fd, cbuf, len, flags);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
} while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } while (outlen < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
@ -2853,10 +2873,11 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS
memset(&addrbuf, 0, addrlen); memset(&addrbuf, 0, addrlen);
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
@ -2866,8 +2887,8 @@ sock_recvfrom_guts(PySocketSockObject *s, char* cbuf, Py_ssize_t len, int flags,
n = recvfrom(s->sock_fd, cbuf, len, flags, n = recvfrom(s->sock_fd, cbuf, len, flags,
SAS2SA(&addrbuf), &addrlen); SAS2SA(&addrbuf), &addrlen);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
@ -3054,7 +3075,6 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS;
msg.msg_name = SAS2SA(&addrbuf); msg.msg_name = SAS2SA(&addrbuf);
msg.msg_namelen = addrbuflen; msg.msg_namelen = addrbuflen;
msg.msg_iov = iov; msg.msg_iov = iov;
@ -3062,13 +3082,17 @@ sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
msg.msg_control = controlbuf; msg.msg_control = controlbuf;
msg.msg_controllen = controllen; msg.msg_controllen = controllen;
timeout = internal_select_ex(s, 0, interval); timeout = internal_select_ex(s, 0, interval);
if (!timeout)
bytes_received = recvmsg(s->sock_fd, &msg, flags);
Py_END_ALLOW_THREADS;
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
goto finally; goto finally;
} }
if (!timeout) {
Py_BEGIN_ALLOW_THREADS;
bytes_received = recvmsg(s->sock_fd, &msg, flags);
Py_END_ALLOW_THREADS;
}
} while (bytes_received < 0 && errno == EINTR && } while (bytes_received < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals())); !(async_err = PyErr_CheckSignals()));
END_SELECT_LOOP(s) END_SELECT_LOOP(s)
@ -3350,9 +3374,10 @@ sock_send(PySocketSockObject *s, PyObject *args)
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 1, interval); timeout = internal_select_ex(s, 1, interval);
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
@ -3360,8 +3385,8 @@ sock_send(PySocketSockObject *s, PyObject *args)
#else #else
n = send(s->sock_fd, buf, len, flags); n = send(s->sock_fd, buf, len, flags);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
@ -3406,10 +3431,11 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
} }
do { do {
Py_BEGIN_ALLOW_THREADS
timeout = internal_select(s, 1); timeout = internal_select(s, 1);
n = -1; n = -1;
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
@ -3417,8 +3443,8 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
#else #else
n = send(s->sock_fd, buf, len, flags); n = send(s->sock_fd, buf, len, flags);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
if (timeout == 1) { if (timeout == 1) {
PyBuffer_Release(&pbuf); PyBuffer_Release(&pbuf);
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
@ -3495,9 +3521,10 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS
timeout = internal_select_ex(s, 1, interval); timeout = internal_select_ex(s, 1, interval);
if (!timeout) { if (!timeout) {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (len > INT_MAX) if (len > INT_MAX)
len = INT_MAX; len = INT_MAX;
@ -3507,8 +3534,8 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
n = sendto(s->sock_fd, buf, len, flags, n = sendto(s->sock_fd, buf, len, flags,
SAS2SA(&addrbuf), addrlen); SAS2SA(&addrbuf), addrlen);
#endif #endif
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
}
} while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (timeout == 1) { if (timeout == 1) {
@ -3710,11 +3737,14 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
BEGIN_SELECT_LOOP(s) BEGIN_SELECT_LOOP(s)
do { do {
Py_BEGIN_ALLOW_THREADS;
timeout = internal_select_ex(s, 1, interval); timeout = internal_select_ex(s, 1, interval);
if (!timeout)
if (!timeout) {
Py_BEGIN_ALLOW_THREADS;
bytes_sent = sendmsg(s->sock_fd, &msg, flags); bytes_sent = sendmsg(s->sock_fd, &msg, flags);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
}
if (timeout == 1) { if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out"); PyErr_SetString(socket_timeout, "timed out");
goto finally; goto finally;