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:
Victor Stinner 2015-04-03 13:22:27 +02:00
parent 13019fdef3
commit 10550cdb8a
1 changed files with 14 additions and 28 deletions

View File

@ -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;
} }