Issue #23834: Simplify timeout handling
* Use the new _PyTime_FromSeconds() function to set the timeout to -1 second for socket.settimeout(None). It avoids a special case in internal_select() because of a rounding issue: -1 nanosecond is rounded to 0 millisecond which means non-blocking, instead of blocking. * Check if the interval the negative in sock_call_ex() instead of doing the check in internal_select(). sock_call_ex() remembers if the socket has a timeout or not, which avoids a race condition if the timeout is modified in a different thread.
This commit is contained in:
parent
13019fdef3
commit
10550cdb8a
|
@ -629,17 +629,6 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval,
|
||||||
if (s->sock_fd < 0)
|
if (s->sock_fd < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* for connect(), we want to poll even if the socket is blocking */
|
|
||||||
if (!connect) {
|
|
||||||
/* Nothing to do unless we're in timeout mode (not non-blocking) */
|
|
||||||
if (s->sock_timeout <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Handling this condition here simplifies the select loops */
|
|
||||||
if (interval < 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prefer poll, if available, since you can poll() any fd
|
/* Prefer poll, if available, since you can poll() any fd
|
||||||
* which can't be done with select(). */
|
* which can't be done with select(). */
|
||||||
#ifdef HAVE_POLL
|
#ifdef HAVE_POLL
|
||||||
|
@ -654,22 +643,14 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s->sock_timeout is in seconds, timeout in ms */
|
/* s->sock_timeout is in seconds, timeout in ms */
|
||||||
if (interval >= 0)
|
ms = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_CEILING);
|
||||||
ms = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_CEILING);
|
|
||||||
else
|
|
||||||
ms = -1;
|
|
||||||
assert(ms <= INT_MAX);
|
assert(ms <= INT_MAX);
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
n = poll(&pollfd, 1, (int)ms);
|
n = poll(&pollfd, 1, (int)ms);
|
||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
#else
|
#else
|
||||||
if (interval >= 0)
|
_PyTime_AsTimeval_noraise(interval, &tv, _PyTime_ROUND_CEILING);
|
||||||
_PyTime_AsTimeval_noraise(interval, &tv, _PyTime_ROUND_CEILING);
|
|
||||||
else {
|
|
||||||
tv.tv_sec = -1;
|
|
||||||
tv.tv_sec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(s->sock_fd, &fds);
|
FD_SET(s->sock_fd, &fds);
|
||||||
|
@ -741,9 +722,9 @@ sock_call_ex(PySocketSockObject *s,
|
||||||
/* For connect(), poll even for blocking socket. The connection
|
/* For connect(), poll even for blocking socket. The connection
|
||||||
runs asynchronously. */
|
runs asynchronously. */
|
||||||
if (has_timeout || connect) {
|
if (has_timeout || connect) {
|
||||||
_PyTime_t interval;
|
|
||||||
|
|
||||||
if (has_timeout) {
|
if (has_timeout) {
|
||||||
|
_PyTime_t interval;
|
||||||
|
|
||||||
if (deadline_initialized) {
|
if (deadline_initialized) {
|
||||||
/* recompute the timeout */
|
/* recompute the timeout */
|
||||||
interval = deadline - _PyTime_GetMonotonicClock();
|
interval = deadline - _PyTime_GetMonotonicClock();
|
||||||
|
@ -753,11 +734,16 @@ sock_call_ex(PySocketSockObject *s,
|
||||||
deadline = _PyTime_GetMonotonicClock() + s->sock_timeout;
|
deadline = _PyTime_GetMonotonicClock() + s->sock_timeout;
|
||||||
interval = s->sock_timeout;
|
interval = s->sock_timeout;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
interval = -1;
|
|
||||||
|
|
||||||
res = internal_select(s, writing, interval, connect);
|
if (interval >= 0)
|
||||||
|
res = internal_select(s, writing, interval, connect);
|
||||||
|
else
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = internal_select(s, writing, -1, connect);
|
||||||
|
}
|
||||||
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
if (err)
|
if (err)
|
||||||
*err = GET_SOCK_ERROR;
|
*err = GET_SOCK_ERROR;
|
||||||
|
@ -2332,7 +2318,7 @@ socket_parse_timeout(_PyTime_t *timeout, PyObject *timeout_obj)
|
||||||
int overflow = 0;
|
int overflow = 0;
|
||||||
|
|
||||||
if (timeout_obj == Py_None) {
|
if (timeout_obj == Py_None) {
|
||||||
*timeout = -1;
|
*timeout = _PyTime_FromSeconds(-1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue