summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-03-28 00:18:54 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-03-28 00:18:54 (GMT)
commit71694d5c8c1846c6ac755bef69d7817f95359ecd (patch)
tree2ecf438ae2dde857ea398f163e31c0fb6c5c7421
parent95e9cef6f023a1cf365f2f02775badb3a6ac0d82 (diff)
downloadcpython-71694d5c8c1846c6ac755bef69d7817f95359ecd.zip
cpython-71694d5c8c1846c6ac755bef69d7817f95359ecd.tar.gz
cpython-71694d5c8c1846c6ac755bef69d7817f95359ecd.tar.bz2
Issue #22117: The socket module uses _PyTime_t timestamp for timeouts
-rw-r--r--Modules/socketmodule.c213
-rw-r--r--Modules/socketmodule.h2
2 files changed, 124 insertions, 91 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index f7e9042..93dcd41 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -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 = (int)(interval * 1000 + 0.5);
+ /* s->sock_timeout is in seconds, timeout in ms */
+ timeout = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_UP);
+ assert(timeout <= INT_MAX);
+ timeout_int = (int)timeout;
- 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);
-
- /* 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;
- }
+ /* conversion was already checked for overflow when
+ the timeout was set */
+ (void)_PyTime_AsTimeval(interval, &tv, _PyTime_ROUND_UP);
+
+ 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;
diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h
index 4b6a10e..3cce927 100644
--- a/Modules/socketmodule.h
+++ b/Modules/socketmodule.h
@@ -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;