Issue #22117: The socket module uses _PyTime_t timestamp for timeouts

This commit is contained in:
Victor Stinner 2015-03-28 01:18:54 +01:00
parent 95e9cef6f0
commit 71694d5c8c
2 changed files with 123 additions and 90 deletions

View File

@ -460,7 +460,7 @@ static PyTypeObject sock_type;
#else
/* If there's no timeout left, we don't have to call select, so it's a safe,
* little white lie. */
#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0.0)
#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0)
#endif
static PyObject*
@ -597,9 +597,17 @@ internal_setblocking(PySocketSockObject *s, int block)
after they've reacquired the interpreter lock.
Returns 1 on timeout, -1 on error, 0 otherwise. */
static int
internal_select_ex(PySocketSockObject *s, int writing, double interval)
internal_select_ex(PySocketSockObject *s, int writing, _PyTime_t interval)
{
int n;
#ifdef HAVE_POLL
struct pollfd pollfd;
_PyTime_t timeout;
int timeout_int;
#else
fd_set fds;
struct timeval tv;
#endif
#ifdef WITH_THREAD
/* must be called with the GIL held */
@ -607,7 +615,7 @@ internal_select_ex(PySocketSockObject *s, int writing, double interval)
#endif
/* Nothing to do unless we're in timeout mode (not non-blocking) */
if (s->sock_timeout <= 0.0)
if (s->sock_timeout <= 0)
return 0;
/* Guard against closed socket */
@ -615,46 +623,40 @@ internal_select_ex(PySocketSockObject *s, int writing, double interval)
return 0;
/* Handling this condition here simplifies the select loops */
if (interval < 0.0)
if (interval < 0)
return 1;
/* Prefer poll, if available, since you can poll() any fd
* which can't be done with select(). */
#ifdef HAVE_POLL
{
struct pollfd pollfd;
int timeout;
pollfd.fd = s->sock_fd;
pollfd.events = writing ? POLLOUT : POLLIN;
pollfd.fd = s->sock_fd;
pollfd.events = writing ? POLLOUT : POLLIN;
/* s->sock_timeout is in seconds, timeout in ms */
timeout = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_UP);
assert(timeout <= INT_MAX);
timeout_int = (int)timeout;
/* s->sock_timeout is in seconds, timeout in ms */
timeout = (int)(interval * 1000 + 0.5);
Py_BEGIN_ALLOW_THREADS;
n = poll(&pollfd, 1, timeout);
Py_END_ALLOW_THREADS;
}
Py_BEGIN_ALLOW_THREADS;
n = poll(&pollfd, 1, timeout_int);
Py_END_ALLOW_THREADS;
#else
{
/* Construct the arguments to select */
fd_set fds;
struct timeval tv;
tv.tv_sec = (int)interval;
tv.tv_usec = (int)((interval - tv.tv_sec) * 1e6);
FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds);
/* conversion was already checked for overflow when
the timeout was set */
(void)_PyTime_AsTimeval(interval, &tv, _PyTime_ROUND_UP);
/* See if the socket is ready */
Py_BEGIN_ALLOW_THREADS;
if (writing)
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, NULL, &tv);
else
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
&fds, NULL, NULL, &tv);
Py_END_ALLOW_THREADS;
}
FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds);
/* See if the socket is ready */
Py_BEGIN_ALLOW_THREADS;
if (writing)
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
NULL, &fds, NULL, &tv);
else
n = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int),
&fds, NULL, NULL, &tv);
Py_END_ALLOW_THREADS;
#endif
if (n < 0)
@ -694,14 +696,11 @@ internal_select(PySocketSockObject *s, int writing)
#define BEGIN_SELECT_LOOP(s) \
{ \
_PyTime_timeval now, deadline = {0, 0}; \
double interval = s->sock_timeout; \
int has_timeout = s->sock_timeout > 0.0; \
if (has_timeout) { \
_PyTime_monotonic(&now); \
deadline = now; \
_PyTime_AddDouble(&deadline, s->sock_timeout, _PyTime_ROUND_UP); \
} \
_PyTime_t deadline = 0; \
_PyTime_t interval = s->sock_timeout; \
int has_timeout = (s->sock_timeout > 0); \
if (has_timeout) \
deadline = _PyTime_GetMonotonicClock() + interval; \
while (1) { \
errno = 0; \
@ -709,14 +708,13 @@ internal_select(PySocketSockObject *s, int writing)
if (!has_timeout || \
(!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
break; \
_PyTime_monotonic(&now); \
interval = _PyTime_INTERVAL(now, deadline); \
interval = deadline - _PyTime_GetMonotonicClock(); \
} \
} \
/* Initialize a new socket object. */
static double defaulttimeout = -1.0; /* Default timeout for new sockets */
static _PyTime_t defaulttimeout = -1; /* Default timeout for new sockets */
static void
init_sockobject(PySocketSockObject *s,
@ -730,12 +728,12 @@ init_sockobject(PySocketSockObject *s,
s->errorhandler = &set_error;
#ifdef SOCK_NONBLOCK
if (type & SOCK_NONBLOCK)
s->sock_timeout = 0.0;
s->sock_timeout = 0;
else
#endif
{
s->sock_timeout = defaulttimeout;
if (defaulttimeout >= 0.0)
if (defaulttimeout >= 0)
internal_setblocking(s, 0);
}
@ -2168,7 +2166,7 @@ sock_setblocking(PySocketSockObject *s, PyObject *arg)
if (block == -1 && PyErr_Occurred())
return NULL;
s->sock_timeout = block ? -1.0 : 0.0;
s->sock_timeout = block ? -1 : 0;
internal_setblocking(s, block);
Py_INCREF(Py_None);
@ -2182,6 +2180,43 @@ Set the socket to blocking (flag is true) or non-blocking (false).\n\
setblocking(True) is equivalent to settimeout(None);\n\
setblocking(False) is equivalent to settimeout(0.0).");
static int
socket_parse_timeout(_PyTime_t *timeout, PyObject *timeout_obj)
{
#ifdef MS_WINDOWS
struct timeval tv;
#endif
int overflow = 0;
if (timeout_obj == Py_None) {
*timeout = -1;
return 0;
}
if (_PyTime_FromSecondsObject(timeout, timeout_obj, _PyTime_ROUND_UP) < 0)
return -1;
if (*timeout < 0) {
PyErr_SetString(PyExc_ValueError, "Timeout value out of range");
return -1;
}
#ifdef MS_WINDOWS
overflow = (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_UP) < 0);
#endif
#ifndef HAVE_POLL
timeout = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_UP);
overflow = (timeout > INT_MAX);
#endif
if (overflow) {
PyErr_SetString(PyExc_OverflowError,
"timeout doesn't fit into C timeval");
return -1;
}
return 0;
}
/* s.settimeout(timeout) method. Argument:
None -- no timeout, blocking mode; same as setblocking(True)
0.0 -- non-blocking mode; same as setblocking(False)
@ -2191,22 +2226,13 @@ setblocking(False) is equivalent to settimeout(0.0).");
static PyObject *
sock_settimeout(PySocketSockObject *s, PyObject *arg)
{
double timeout;
_PyTime_t timeout;
if (arg == Py_None)
timeout = -1.0;
else {
timeout = PyFloat_AsDouble(arg);
if (timeout < 0.0) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_ValueError,
"Timeout value out of range");
return NULL;
}
}
if (socket_parse_timeout(&timeout, arg) < 0)
return NULL;
s->sock_timeout = timeout;
internal_setblocking(s, timeout < 0.0);
internal_setblocking(s, timeout < 0);
Py_INCREF(Py_None);
return Py_None;
@ -2225,12 +2251,14 @@ Setting a timeout of zero is the same as setblocking(0).");
static PyObject *
sock_gettimeout(PySocketSockObject *s)
{
if (s->sock_timeout < 0.0) {
if (s->sock_timeout < 0) {
Py_INCREF(Py_None);
return Py_None;
}
else
return PyFloat_FromDouble(s->sock_timeout);
else {
double seconds = _PyTime_AsSecondsDouble(s->sock_timeout);
return PyFloat_FromDouble(seconds);
}
}
PyDoc_STRVAR(gettimeout_doc,
@ -2409,27 +2437,28 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
{
int res, timeout;
timeout = 0;
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
#ifdef MS_WINDOWS
if (s->sock_timeout > 0.0
if (s->sock_timeout > 0
&& res < 0 && WSAGetLastError() == WSAEWOULDBLOCK
&& IS_SELECTABLE(s)) {
/* This is a mess. Best solution: trust select */
fd_set fds;
fd_set fds_exc;
struct timeval tv;
int conv;
/* conversion was already checked for overflow when
the timeout was set */
(void)_PyTime_AsTimeval(s->sock_timeout, &tv, _PyTime_ROUND_UP);
Py_BEGIN_ALLOW_THREADS
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);
@ -2469,7 +2498,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
#else
if (s->sock_timeout > 0.0
if (s->sock_timeout > 0
&& res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
timeout = internal_select(s, 1);
@ -2498,6 +2527,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
#endif
*timeoutp = timeout;
assert(res >= 0);
return res;
}
@ -2520,8 +2550,11 @@ sock_connect(PySocketSockObject *s, PyObject *addro)
PyErr_SetString(socket_timeout, "timed out");
return NULL;
}
if (res != 0)
if (res < 0)
return NULL;
if (res != 0) {
return s->errorhandler();
}
Py_INCREF(Py_None);
return Py_None;
}
@ -2548,6 +2581,9 @@ sock_connect_ex(PySocketSockObject *s, PyObject *addro)
res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout);
if (res < 0)
return NULL;
/* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */
if (res == EINTR && PyErr_CheckSignals())
@ -3967,10 +4003,14 @@ static PyMemberDef sock_memberlist[] = {
{"family", T_INT, offsetof(PySocketSockObject, sock_family), READONLY, "the socket family"},
{"type", T_INT, offsetof(PySocketSockObject, sock_type), READONLY, "the socket type"},
{"proto", T_INT, offsetof(PySocketSockObject, sock_proto), READONLY, "the socket protocol"},
{"timeout", T_DOUBLE, offsetof(PySocketSockObject, sock_timeout), READONLY, "the socket timeout"},
{0},
};
static PyGetSetDef sock_getsetlist[] = {
{"timeout", (getter)sock_gettimeout, NULL, PyDoc_STR("the socket timeout")},
{NULL} /* sentinel */
};
/* Deallocate a socket object in response to the last Py_DECREF().
First close the file description. */
@ -4034,7 +4074,7 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
new = type->tp_alloc(type, 0);
if (new != NULL) {
((PySocketSockObject *)new)->sock_fd = -1;
((PySocketSockObject *)new)->sock_timeout = -1.0;
((PySocketSockObject *)new)->sock_timeout = -1;
((PySocketSockObject *)new)->errorhandler = &set_error;
}
return new;
@ -4217,7 +4257,7 @@ static PyTypeObject sock_type = {
0, /* tp_iternext */
sock_methods, /* tp_methods */
sock_memberlist, /* tp_members */
0, /* tp_getset */
sock_getsetlist, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
@ -5540,12 +5580,14 @@ Get host and port for a sockaddr.");
static PyObject *
socket_getdefaulttimeout(PyObject *self)
{
if (defaulttimeout < 0.0) {
if (defaulttimeout < 0) {
Py_INCREF(Py_None);
return Py_None;
}
else
return PyFloat_FromDouble(defaulttimeout);
else {
double seconds = _PyTime_AsSecondsDouble(defaulttimeout);
return PyFloat_FromDouble(seconds);
}
}
PyDoc_STRVAR(getdefaulttimeout_doc,
@ -5558,19 +5600,10 @@ When the socket module is first imported, the default is None.");
static PyObject *
socket_setdefaulttimeout(PyObject *self, PyObject *arg)
{
double timeout;
_PyTime_t timeout;
if (arg == Py_None)
timeout = -1.0;
else {
timeout = PyFloat_AsDouble(arg);
if (timeout < 0.0) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_ValueError,
"Timeout value out of range");
return NULL;
}
}
if (socket_parse_timeout(&timeout, arg) < 0)
return NULL;
defaulttimeout = timeout;

View File

@ -174,7 +174,7 @@ typedef struct {
PyObject *(*errorhandler)(void); /* Error handler; checks
errno, returns NULL and
sets a Python exception */
double sock_timeout; /* Operation timeout in seconds;
_PyTime_t sock_timeout; /* Operation timeout in seconds;
0.0 means non-blocking */
} PySocketSockObject;