summaryrefslogtreecommitdiff
path: root/Modules/selectmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/selectmodule.c')
-rw-r--r--Modules/selectmodule.c465
1 files changed, 286 insertions, 179 deletions
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index ffaf865df2..590aeca31b 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -193,51 +193,32 @@ select_select(PyObject *self, PyObject *args)
#endif /* SELECT_USES_HEAP */
PyObject *ifdlist, *ofdlist, *efdlist;
PyObject *ret = NULL;
- PyObject *tout = Py_None;
+ PyObject *timeout_obj = Py_None;
fd_set ifdset, ofdset, efdset;
struct timeval tv, *tvp;
int imax, omax, emax, max;
int n;
+ _PyTime_t timeout, deadline = 0;
/* convert arguments */
if (!PyArg_UnpackTuple(args, "select", 3, 4,
- &ifdlist, &ofdlist, &efdlist, &tout))
+ &ifdlist, &ofdlist, &efdlist, &timeout_obj))
return NULL;
- if (tout == Py_None)
- tvp = (struct timeval *)0;
- else if (!PyNumber_Check(tout)) {
- PyErr_SetString(PyExc_TypeError,
- "timeout must be a float or None");
- return NULL;
- }
+ if (timeout_obj == Py_None)
+ tvp = (struct timeval *)NULL;
else {
- /* On OpenBSD 5.4, timeval.tv_sec is a long.
- * Example: long is 64-bit, whereas time_t is 32-bit. */
- time_t sec;
- /* On OS X 64-bit, timeval.tv_usec is an int (and thus still 4
- bytes as required), but no longer defined by a long. */
- long usec;
- if (_PyTime_ObjectToTimeval(tout, &sec, &usec,
- _PyTime_ROUND_UP) == -1)
- return NULL;
-#ifdef MS_WINDOWS
- /* On Windows, timeval.tv_sec is a long (32 bit),
- * whereas time_t can be 64-bit. */
- assert(sizeof(tv.tv_sec) == sizeof(long));
-#if SIZEOF_TIME_T > SIZEOF_LONG
- if (sec > LONG_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout is too large");
+ if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
+ _PyTime_ROUND_CEILING) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_SetString(PyExc_TypeError,
+ "timeout must be a float or None");
+ }
return NULL;
}
-#endif
- tv.tv_sec = (long)sec;
-#else
- assert(sizeof(tv.tv_sec) >= sizeof(sec));
- tv.tv_sec = sec;
-#endif
- tv.tv_usec = usec;
+
+ if (_PyTime_AsTimeval(timeout, &tv, _PyTime_ROUND_CEILING) == -1)
+ return NULL;
if (tv.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
return NULL;
@@ -245,7 +226,6 @@ select_select(PyObject *self, PyObject *args)
tvp = &tv;
}
-
#ifdef SELECT_USES_HEAP
/* Allocate memory for the lists */
rfd2obj = PyMem_NEW(pylist, FD_SETSIZE + 1);
@@ -258,6 +238,7 @@ select_select(PyObject *self, PyObject *args)
return PyErr_NoMemory();
}
#endif /* SELECT_USES_HEAP */
+
/* Convert sequences to fd_sets, and get maximum fd number
* propagates the Python exception set in seq2set()
*/
@@ -270,13 +251,37 @@ select_select(PyObject *self, PyObject *args)
goto finally;
if ((emax=seq2set(efdlist, &efdset, efd2obj)) < 0)
goto finally;
+
max = imax;
if (omax > max) max = omax;
if (emax > max) max = emax;
- Py_BEGIN_ALLOW_THREADS
- n = select(max, &ifdset, &ofdset, &efdset, tvp);
- Py_END_ALLOW_THREADS
+ if (tvp)
+ deadline = _PyTime_GetMonotonicClock() + timeout;
+
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ errno = 0;
+ n = select(max, &ifdset, &ofdset, &efdset, tvp);
+ Py_END_ALLOW_THREADS
+
+ if (errno != EINTR)
+ break;
+
+ /* select() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ goto finally;
+
+ if (tvp) {
+ timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
+ n = 0;
+ break;
+ }
+ _PyTime_AsTimeval_noraise(timeout, &tv, _PyTime_ROUND_CEILING);
+ /* retry select() with the recomputed timeout */
+ }
+ } while (1);
#ifdef MS_WINDOWS
if (n == SOCKET_ERROR) {
@@ -516,30 +521,39 @@ any descriptors that have events or errors to report.");
static PyObject *
poll_poll(pollObject *self, PyObject *args)
{
- PyObject *result_list = NULL, *tout = NULL;
- int timeout = 0, poll_result, i, j;
+ PyObject *result_list = NULL, *timeout_obj = NULL;
+ int poll_result, i, j;
PyObject *value = NULL, *num = NULL;
+ _PyTime_t timeout, ms, deadline;
+ int async_err = 0;
- if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
+ if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL;
}
/* Check values for timeout */
- if (tout == NULL || tout == Py_None)
+ if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
- else if (!PyNumber_Check(tout)) {
- PyErr_SetString(PyExc_TypeError,
- "timeout must be an integer or None");
- return NULL;
+ ms = -1;
+ deadline = 0; /* initialize to prevent gcc warning */
}
else {
- tout = PyNumber_Long(tout);
- if (!tout)
+ if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
+ _PyTime_ROUND_CEILING) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_SetString(PyExc_TypeError,
+ "timeout must be an integer or None");
+ }
return NULL;
- timeout = _PyLong_AsInt(tout);
- Py_DECREF(tout);
- if (timeout == -1 && PyErr_Occurred())
+ }
+
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ if (ms < INT_MIN || ms > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "timeout is too large");
return NULL;
+ }
+
+ deadline = _PyTime_GetMonotonicClock() + timeout;
}
/* Avoid concurrent poll() invocation, issue 8865 */
@@ -557,14 +571,38 @@ poll_poll(pollObject *self, PyObject *args)
self->poll_running = 1;
/* call poll() */
- Py_BEGIN_ALLOW_THREADS
- poll_result = poll(self->ufds, self->ufd_len, timeout);
- Py_END_ALLOW_THREADS
+ async_err = 0;
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ errno = 0;
+ poll_result = poll(self->ufds, self->ufd_len, (int)ms);
+ Py_END_ALLOW_THREADS
+
+ if (errno != EINTR)
+ break;
+
+ /* poll() was interrupted by a signal */
+ if (PyErr_CheckSignals()) {
+ async_err = 1;
+ break;
+ }
+
+ if (timeout >= 0) {
+ timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
+ poll_result = 0;
+ break;
+ }
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ /* retry poll() with the recomputed timeout */
+ }
+ } while (1);
self->poll_running = 0;
if (poll_result < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
+ if (!async_err)
+ PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
@@ -573,41 +611,40 @@ poll_poll(pollObject *self, PyObject *args)
result_list = PyList_New(poll_result);
if (!result_list)
return NULL;
- else {
- for (i = 0, j = 0; j < poll_result; j++) {
- /* skip to the next fired descriptor */
- while (!self->ufds[i].revents) {
- i++;
- }
- /* if we hit a NULL return, set value to NULL
- and break out of loop; code at end will
- clean up result_list */
- value = PyTuple_New(2);
- if (value == NULL)
- goto error;
- num = PyLong_FromLong(self->ufds[i].fd);
- if (num == NULL) {
- Py_DECREF(value);
- goto error;
- }
- PyTuple_SET_ITEM(value, 0, num);
-
- /* The &0xffff is a workaround for AIX. 'revents'
- is a 16-bit short, and IBM assigned POLLNVAL
- to be 0x8000, so the conversion to int results
- in a negative number. See SF bug #923315. */
- num = PyLong_FromLong(self->ufds[i].revents & 0xffff);
- if (num == NULL) {
- Py_DECREF(value);
- goto error;
- }
- PyTuple_SET_ITEM(value, 1, num);
- if ((PyList_SetItem(result_list, j, value)) == -1) {
- Py_DECREF(value);
- goto error;
- }
+
+ for (i = 0, j = 0; j < poll_result; j++) {
+ /* skip to the next fired descriptor */
+ while (!self->ufds[i].revents) {
i++;
}
+ /* if we hit a NULL return, set value to NULL
+ and break out of loop; code at end will
+ clean up result_list */
+ value = PyTuple_New(2);
+ if (value == NULL)
+ goto error;
+ num = PyLong_FromLong(self->ufds[i].fd);
+ if (num == NULL) {
+ Py_DECREF(value);
+ goto error;
+ }
+ PyTuple_SET_ITEM(value, 0, num);
+
+ /* The &0xffff is a workaround for AIX. 'revents'
+ is a 16-bit short, and IBM assigned POLLNVAL
+ to be 0x8000, so the conversion to int results
+ in a negative number. See SF bug #923315. */
+ num = PyLong_FromLong(self->ufds[i].revents & 0xffff);
+ if (num == NULL) {
+ Py_DECREF(value);
+ goto error;
+ }
+ PyTuple_SET_ITEM(value, 1, num);
+ if ((PyList_SetItem(result_list, j, value)) == -1) {
+ Py_DECREF(value);
+ goto error;
+ }
+ i++;
}
return result_list;
@@ -718,14 +755,10 @@ static int devpoll_flush(devpollObject *self)
size = sizeof(struct pollfd)*self->n_fds;
self->n_fds = 0;
- Py_BEGIN_ALLOW_THREADS
- n = write(self->fd_devpoll, self->fds, size);
- Py_END_ALLOW_THREADS
-
- if (n == -1 ) {
- PyErr_SetFromErrno(PyExc_IOError);
+ n = _Py_write(self->fd_devpoll, self->fds, size);
+ if (n == -1)
return -1;
- }
+
if (n < size) {
/*
** Data writed to /dev/poll is a binary data structure. It is not
@@ -843,40 +876,38 @@ static PyObject *
devpoll_poll(devpollObject *self, PyObject *args)
{
struct dvpoll dvp;
- PyObject *result_list = NULL, *tout = NULL;
+ PyObject *result_list = NULL, *timeout_obj = NULL;
int poll_result, i;
- long timeout;
PyObject *value, *num1, *num2;
+ _PyTime_t timeout, ms, deadline = 0;
if (self->fd_devpoll < 0)
return devpoll_err_closed();
- if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) {
+ if (!PyArg_ParseTuple(args, "|O:poll", &timeout_obj)) {
return NULL;
}
/* Check values for timeout */
- if (tout == NULL || tout == Py_None)
+ if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
- else if (!PyNumber_Check(tout)) {
- PyErr_SetString(PyExc_TypeError,
- "timeout must be an integer or None");
- return NULL;
+ ms = -1;
}
else {
- tout = PyNumber_Long(tout);
- if (!tout)
- return NULL;
- timeout = PyLong_AsLong(tout);
- Py_DECREF(tout);
- if (timeout == -1 && PyErr_Occurred())
+ if (_PyTime_FromMillisecondsObject(&timeout, timeout_obj,
+ _PyTime_ROUND_CEILING) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_SetString(PyExc_TypeError,
+ "timeout must be an integer or None");
+ }
return NULL;
- }
+ }
- if ((timeout < -1) || (timeout > INT_MAX)) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout is out of range");
- return NULL;
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ if (ms < -1 || ms > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "timeout is too large");
+ return NULL;
+ }
}
if (devpoll_flush(self))
@@ -884,12 +915,36 @@ devpoll_poll(devpollObject *self, PyObject *args)
dvp.dp_fds = self->fds;
dvp.dp_nfds = self->max_n_fds;
- dvp.dp_timeout = timeout;
+ dvp.dp_timeout = (int)ms;
- /* call devpoll() */
- Py_BEGIN_ALLOW_THREADS
- poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
- Py_END_ALLOW_THREADS
+ if (timeout >= 0)
+ deadline = _PyTime_GetMonotonicClock() + timeout;
+
+ do {
+ /* call devpoll() */
+ Py_BEGIN_ALLOW_THREADS
+ errno = 0;
+ poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp);
+ Py_END_ALLOW_THREADS
+
+ if (errno != EINTR)
+ break;
+
+ /* devpoll() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ return NULL;
+
+ if (timeout >= 0) {
+ timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
+ poll_result = 0;
+ break;
+ }
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ dvp.dp_timeout = (int)ms;
+ /* retry devpoll() with the recomputed timeout */
+ }
+ } while (1);
if (poll_result < 0) {
PyErr_SetFromErrno(PyExc_IOError);
@@ -897,28 +952,26 @@ devpoll_poll(devpollObject *self, PyObject *args)
}
/* build the result list */
-
result_list = PyList_New(poll_result);
if (!result_list)
return NULL;
- else {
- for (i = 0; i < poll_result; i++) {
- num1 = PyLong_FromLong(self->fds[i].fd);
- num2 = PyLong_FromLong(self->fds[i].revents);
- if ((num1 == NULL) || (num2 == NULL)) {
- Py_XDECREF(num1);
- Py_XDECREF(num2);
- goto error;
- }
- value = PyTuple_Pack(2, num1, num2);
- Py_DECREF(num1);
- Py_DECREF(num2);
- if (value == NULL)
- goto error;
- if ((PyList_SetItem(result_list, i, value)) == -1) {
- Py_DECREF(value);
- goto error;
- }
+
+ for (i = 0; i < poll_result; i++) {
+ num1 = PyLong_FromLong(self->fds[i].fd);
+ num2 = PyLong_FromLong(self->fds[i].revents);
+ if ((num1 == NULL) || (num2 == NULL)) {
+ Py_XDECREF(num1);
+ Py_XDECREF(num2);
+ goto error;
+ }
+ value = PyTuple_Pack(2, num1, num2);
+ Py_DECREF(num1);
+ Py_DECREF(num2);
+ if (value == NULL)
+ goto error;
+ if ((PyList_SetItem(result_list, i, value)) == -1) {
+ Py_DECREF(value);
+ goto error;
}
}
@@ -1013,7 +1066,6 @@ newDevPollObject(void)
struct pollfd *fds;
struct rlimit limit;
- Py_BEGIN_ALLOW_THREADS
/*
** If we try to process more that getrlimit()
** fds, the kernel will give an error, so
@@ -1021,18 +1073,14 @@ newDevPollObject(void)
** value, because we can change rlimit() anytime.
*/
limit_result = getrlimit(RLIMIT_NOFILE, &limit);
- if (limit_result != -1)
- fd_devpoll = _Py_open("/dev/poll", O_RDWR);
- Py_END_ALLOW_THREADS
-
if (limit_result == -1) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
- if (fd_devpoll == -1) {
- PyErr_SetFromErrnoWithFilename(PyExc_IOError, "/dev/poll");
+
+ fd_devpoll = _Py_open("/dev/poll", O_RDWR);
+ if (fd_devpoll == -1)
return NULL;
- }
fds = PyMem_NEW(struct pollfd, limit.rlim_cur);
if (fds == NULL) {
@@ -1437,34 +1485,46 @@ fd is the target file descriptor of the operation.");
static PyObject *
pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
{
- double dtimeout = -1.;
- int timeout;
+ static char *kwlist[] = {"timeout", "maxevents", NULL};
+ PyObject *timeout_obj = NULL;
int maxevents = -1;
int nfds, i;
PyObject *elist = NULL, *etuple = NULL;
struct epoll_event *evs = NULL;
- static char *kwlist[] = {"timeout", "maxevents", NULL};
+ _PyTime_t timeout, ms, deadline;
if (self->epfd < 0)
return pyepoll_err_closed();
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|di:poll", kwlist,
- &dtimeout, &maxevents)) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:poll", kwlist,
+ &timeout_obj, &maxevents)) {
return NULL;
}
- if (dtimeout < 0) {
+ if (timeout_obj == NULL || timeout_obj == Py_None) {
timeout = -1;
- }
- else if (dtimeout * 1000.0 > INT_MAX) {
- PyErr_SetString(PyExc_OverflowError,
- "timeout is too large");
- return NULL;
+ ms = -1;
+ deadline = 0; /* initialize to prevent gcc warning */
}
else {
- /* epoll_wait() has a resolution of 1 millisecond, round away from zero
- to wait *at least* dtimeout seconds. */
- timeout = (int)ceil(dtimeout * 1000.0);
+ /* epoll_wait() has a resolution of 1 millisecond, round towards
+ infinity to wait at least timeout seconds. */
+ if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
+ _PyTime_ROUND_CEILING) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_SetString(PyExc_TypeError,
+ "timeout must be an integer or None");
+ }
+ return NULL;
+ }
+
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ if (ms < INT_MIN || ms > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "timeout is too large");
+ return NULL;
+ }
+
+ deadline = _PyTime_GetMonotonicClock() + timeout;
}
if (maxevents == -1) {
@@ -1483,9 +1543,30 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
return NULL;
}
- Py_BEGIN_ALLOW_THREADS
- nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
- Py_END_ALLOW_THREADS
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ errno = 0;
+ nfds = epoll_wait(self->epfd, evs, maxevents, (int)ms);
+ Py_END_ALLOW_THREADS
+
+ if (errno != EINTR)
+ break;
+
+ /* poll() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ goto error;
+
+ if (timeout >= 0) {
+ timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
+ nfds = 0;
+ break;
+ }
+ ms = _PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
+ /* retry epoll_wait() with the recomputed timeout */
+ }
+ } while(1);
+
if (nfds < 0) {
PyErr_SetFromErrno(PyExc_OSError);
goto error;
@@ -2022,8 +2103,9 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
PyObject *result = NULL;
struct kevent *evl = NULL;
struct kevent *chl = NULL;
- struct timespec timeout;
+ struct timespec timeoutspec;
struct timespec *ptimeoutspec;
+ _PyTime_t timeout, deadline = 0;
if (self->kqfd < 0)
return kqueue_queue_err_closed();
@@ -2041,24 +2123,25 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
if (otimeout == Py_None || otimeout == NULL) {
ptimeoutspec = NULL;
}
- else if (PyNumber_Check(otimeout)) {
- if (_PyTime_ObjectToTimespec(otimeout, &timeout.tv_sec,
- &timeout.tv_nsec, _PyTime_ROUND_UP) == -1)
+ else {
+ if (_PyTime_FromSecondsObject(&timeout,
+ otimeout, _PyTime_ROUND_CEILING) < 0) {
+ PyErr_Format(PyExc_TypeError,
+ "timeout argument must be an number "
+ "or None, got %.200s",
+ Py_TYPE(otimeout)->tp_name);
return NULL;
+ }
- if (timeout.tv_sec < 0) {
+ if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
+ return NULL;
+
+ if (timeoutspec.tv_sec < 0) {
PyErr_SetString(PyExc_ValueError,
"timeout must be positive or None");
return NULL;
}
- ptimeoutspec = &timeout;
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "timeout argument must be an number "
- "or None, got %.200s",
- Py_TYPE(otimeout)->tp_name);
- return NULL;
+ ptimeoutspec = &timeoutspec;
}
if (ch != NULL && ch != Py_None) {
@@ -2103,10 +2186,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
}
}
- Py_BEGIN_ALLOW_THREADS
- gotevents = kevent(self->kqfd, chl, nchanges,
- evl, nevents, ptimeoutspec);
- Py_END_ALLOW_THREADS
+ if (ptimeoutspec)
+ deadline = _PyTime_GetMonotonicClock() + timeout;
+
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ errno = 0;
+ gotevents = kevent(self->kqfd, chl, nchanges,
+ evl, nevents, ptimeoutspec);
+ Py_END_ALLOW_THREADS
+
+ if (errno != EINTR)
+ break;
+
+ /* kevent() was interrupted by a signal */
+ if (PyErr_CheckSignals())
+ goto error;
+
+ if (ptimeoutspec) {
+ timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
+ gotevents = 0;
+ break;
+ }
+ if (_PyTime_AsTimespec(timeout, &timeoutspec) == -1)
+ goto error;
+ /* retry kevent() with the recomputed timeout */
+ }
+ } while (1);
if (gotevents == -1) {
PyErr_SetFromErrno(PyExc_OSError);