diff options
Diffstat (limited to 'Modules/selectmodule.c')
-rw-r--r-- | Modules/selectmodule.c | 489 |
1 files changed, 427 insertions, 62 deletions
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 2452a65..9cb7481 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -7,6 +7,14 @@ #include "Python.h" #include <structmember.h> +#ifdef HAVE_SYS_DEVPOLL_H +#include <sys/resource.h> +#include <sys/devpoll.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#endif + #ifdef __APPLE__ /* Perform runtime testing for a broken poll on OSX to make it easier * to use the same binary on multiple releases of the OS. @@ -54,8 +62,6 @@ extern void bzero(void *, int); # endif #endif -static PyObject *SelectError; - /* list of Python objects and their file descriptor */ typedef struct { PyObject *obj; /* owned reference */ @@ -200,9 +206,7 @@ select_select(PyObject *self, PyObject *args) PyObject *ret = NULL; PyObject *tout = Py_None; fd_set ifdset, ofdset, efdset; - double timeout; struct timeval tv, *tvp; - long seconds; int imax, omax, emax, max; int n; @@ -219,18 +223,27 @@ select_select(PyObject *self, PyObject *args) return NULL; } else { - timeout = PyFloat_AsDouble(tout); - if (timeout == -1 && PyErr_Occurred()) +#ifdef MS_WINDOWS + time_t sec; + if (_PyTime_ObjectToTimeval(tout, &sec, &tv.tv_usec) == -1) return NULL; - if (timeout > (double)LONG_MAX) { + assert(sizeof(tv.tv_sec) == sizeof(long)); +#if SIZEOF_TIME_T > SIZEOF_LONG + if (sec > LONG_MAX) { PyErr_SetString(PyExc_OverflowError, - "timeout period too long"); + "timeout is too large"); + return NULL; + } +#endif + tv.tv_sec = (long)sec; +#else + if (_PyTime_ObjectToTimeval(tout, &tv.tv_sec, &tv.tv_usec) == -1) + return NULL; +#endif + if (tv.tv_sec < 0) { + PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; } - seconds = (long)timeout; - timeout = timeout - (double)seconds; - tv.tv_sec = seconds; - tv.tv_usec = (long)(timeout * 1E6); tvp = &tv; } @@ -269,11 +282,11 @@ select_select(PyObject *self, PyObject *args) #ifdef MS_WINDOWS if (n == SOCKET_ERROR) { - PyErr_SetExcFromWindowsErr(SelectError, WSAGetLastError()); + PyErr_SetExcFromWindowsErr(PyExc_OSError, WSAGetLastError()); } #else if (n < 0) { - PyErr_SetFromErrno(SelectError); + PyErr_SetFromErrno(PyExc_OSError); } #endif else { @@ -420,7 +433,7 @@ poll_modify(pollObject *self, PyObject *args) return NULL; if (PyDict_GetItem(self->dict, key) == NULL) { errno = ENOENT; - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } value = PyLong_FromLong(events); @@ -519,7 +532,7 @@ poll_poll(pollObject *self, PyObject *args) Py_END_ALLOW_THREADS if (poll_result < 0) { - PyErr_SetFromErrno(SelectError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -645,6 +658,339 @@ static PyTypeObject poll_Type = { poll_methods, /*tp_methods*/ }; +#ifdef HAVE_SYS_DEVPOLL_H +typedef struct { + PyObject_HEAD + int fd_devpoll; + int max_n_fds; + int n_fds; + struct pollfd *fds; +} devpollObject; + +static PyTypeObject devpoll_Type; + +static int devpoll_flush(devpollObject *self) +{ + int size, n; + + if (!self->n_fds) return 0; + + 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); + return -1; + } + if (n < size) { + /* + ** Data writed to /dev/poll is a binary data structure. It is not + ** clear what to do if a partial write occurred. For now, raise + ** an exception and see if we actually found this problem in + ** the wild. + ** See http://bugs.python.org/issue6397. + */ + PyErr_Format(PyExc_IOError, "failed to write all pollfds. " + "Please, report at http://bugs.python.org/. " + "Data to report: Size tried: %d, actual size written: %d.", + size, n); + return -1; + } + return 0; +} + +static PyObject * +internal_devpoll_register(devpollObject *self, PyObject *args, int remove) +{ + PyObject *o; + int fd, events = POLLIN | POLLPRI | POLLOUT; + + if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) { + return NULL; + } + + fd = PyObject_AsFileDescriptor(o); + if (fd == -1) return NULL; + + if (remove) { + self->fds[self->n_fds].fd = fd; + self->fds[self->n_fds].events = POLLREMOVE; + + if (++self->n_fds == self->max_n_fds) { + if (devpoll_flush(self)) + return NULL; + } + } + + self->fds[self->n_fds].fd = fd; + self->fds[self->n_fds].events = events; + + if (++self->n_fds == self->max_n_fds) { + if (devpoll_flush(self)) + return NULL; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(devpoll_register_doc, +"register(fd [, eventmask] ) -> None\n\n\ +Register a file descriptor with the polling object.\n\ +fd -- either an integer, or an object with a fileno() method returning an\n\ + int.\n\ +events -- an optional bitmask describing the type of events to check for"); + +static PyObject * +devpoll_register(devpollObject *self, PyObject *args) +{ + return internal_devpoll_register(self, args, 0); +} + +PyDoc_STRVAR(devpoll_modify_doc, +"modify(fd[, eventmask]) -> None\n\n\ +Modify a possible already registered file descriptor.\n\ +fd -- either an integer, or an object with a fileno() method returning an\n\ + int.\n\ +events -- an optional bitmask describing the type of events to check for"); + +static PyObject * +devpoll_modify(devpollObject *self, PyObject *args) +{ + return internal_devpoll_register(self, args, 1); +} + + +PyDoc_STRVAR(devpoll_unregister_doc, +"unregister(fd) -> None\n\n\ +Remove a file descriptor being tracked by the polling object."); + +static PyObject * +devpoll_unregister(devpollObject *self, PyObject *o) +{ + int fd; + + fd = PyObject_AsFileDescriptor( o ); + if (fd == -1) + return NULL; + + self->fds[self->n_fds].fd = fd; + self->fds[self->n_fds].events = POLLREMOVE; + + if (++self->n_fds == self->max_n_fds) { + if (devpoll_flush(self)) + return NULL; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(devpoll_poll_doc, +"poll( [timeout] ) -> list of (fd, event) 2-tuples\n\n\ +Polls the set of registered file descriptors, returning a list containing \n\ +any descriptors that have events or errors to report."); + +static PyObject * +devpoll_poll(devpollObject *self, PyObject *args) +{ + struct dvpoll dvp; + PyObject *result_list = NULL, *tout = NULL; + int poll_result, i; + long timeout; + PyObject *value, *num1, *num2; + + if (!PyArg_UnpackTuple(args, "poll", 0, 1, &tout)) { + return NULL; + } + + /* Check values for timeout */ + if (tout == NULL || tout == Py_None) + timeout = -1; + else if (!PyNumber_Check(tout)) { + PyErr_SetString(PyExc_TypeError, + "timeout must be an integer or None"); + return NULL; + } + else { + tout = PyNumber_Long(tout); + if (!tout) + return NULL; + timeout = PyLong_AsLong(tout); + Py_DECREF(tout); + if (timeout == -1 && PyErr_Occurred()) + return NULL; + } + + if ((timeout < -1) || (timeout > INT_MAX)) { + PyErr_SetString(PyExc_OverflowError, + "timeout is out of range"); + return NULL; + } + + if (devpoll_flush(self)) + return NULL; + + dvp.dp_fds = self->fds; + dvp.dp_nfds = self->max_n_fds; + dvp.dp_timeout = timeout; + + /* call devpoll() */ + Py_BEGIN_ALLOW_THREADS + poll_result = ioctl(self->fd_devpoll, DP_POLL, &dvp); + Py_END_ALLOW_THREADS + + if (poll_result < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + + /* 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; + } + } + } + + return result_list; + + error: + Py_DECREF(result_list); + return NULL; +} + +static PyMethodDef devpoll_methods[] = { + {"register", (PyCFunction)devpoll_register, + METH_VARARGS, devpoll_register_doc}, + {"modify", (PyCFunction)devpoll_modify, + METH_VARARGS, devpoll_modify_doc}, + {"unregister", (PyCFunction)devpoll_unregister, + METH_O, devpoll_unregister_doc}, + {"poll", (PyCFunction)devpoll_poll, + METH_VARARGS, devpoll_poll_doc}, + {NULL, NULL} /* sentinel */ +}; + +static devpollObject * +newDevPollObject(void) +{ + devpollObject *self; + int fd_devpoll, limit_result; + 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 + ** we set the limit here. It is a dynamic + ** value, because we can change rlimit() anytime. + */ + limit_result = getrlimit(RLIMIT_NOFILE, &limit); + if (limit_result != -1) + fd_devpoll = 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"); + return NULL; + } + + fds = PyMem_NEW(struct pollfd, limit.rlim_cur); + if (fds == NULL) { + close(fd_devpoll); + PyErr_NoMemory(); + return NULL; + } + + self = PyObject_New(devpollObject, &devpoll_Type); + if (self == NULL) { + close(fd_devpoll); + PyMem_DEL(fds); + return NULL; + } + self->fd_devpoll = fd_devpoll; + self->max_n_fds = limit.rlim_cur; + self->n_fds = 0; + self->fds = fds; + + return self; +} + +static void +devpoll_dealloc(devpollObject *self) +{ + Py_BEGIN_ALLOW_THREADS + close(self->fd_devpoll); + Py_END_ALLOW_THREADS + + PyMem_DEL(self->fds); + + PyObject_Del(self); +} + +static PyTypeObject devpoll_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "select.devpoll", /*tp_name*/ + sizeof(devpollObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)devpoll_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + devpoll_methods, /*tp_methods*/ +}; +#endif /* HAVE_SYS_DEVPOLL_H */ + + + PyDoc_STRVAR(poll_doc, "Returns a polling object, which supports registering and\n\ unregistering file descriptors, and then polling them for I/O events."); @@ -655,6 +1001,19 @@ select_poll(PyObject *self, PyObject *unused) return (PyObject *)newPollObject(); } +#ifdef HAVE_SYS_DEVPOLL_H +PyDoc_STRVAR(devpoll_doc, +"Returns a polling object, which supports registering and\n\ +unregistering file descriptors, and then polling them for I/O events."); + +static PyObject * +select_devpoll(PyObject *self, PyObject *unused) +{ + return (PyObject *)newDevPollObject(); +} +#endif + + #ifdef __APPLE__ /* * On some systems poll() sets errno on invalid file descriptors. We test @@ -730,20 +1089,10 @@ pyepoll_internal_close(pyEpoll_Object *self) } static PyObject * -newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) +newPyEpoll_Object(PyTypeObject *type, int sizehint, int flags, SOCKET fd) { pyEpoll_Object *self; - if (sizehint == -1) { - sizehint = FD_SETSIZE-1; - } - else if (sizehint < 1) { - PyErr_Format(PyExc_ValueError, - "sizehint must be greater zero, got %d", - sizehint); - return NULL; - } - assert(type != NULL && type->tp_alloc != NULL); self = (pyEpoll_Object *) type->tp_alloc(type, 0); if (self == NULL) @@ -751,6 +1100,11 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) if (fd == -1) { Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_EPOLL_CREATE1 + if (flags) + self->epfd = epoll_create1(flags); + else +#endif self->epfd = epoll_create(sizehint); Py_END_ALLOW_THREADS } @@ -759,7 +1113,7 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) } if (self->epfd < 0) { Py_DECREF(self); - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } return (PyObject *)self; @@ -769,14 +1123,18 @@ newPyEpoll_Object(PyTypeObject *type, int sizehint, SOCKET fd) static PyObject * pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - int sizehint = -1; - static char *kwlist[] = {"sizehint", NULL}; + int flags = 0, sizehint = FD_SETSIZE - 1; + static char *kwlist[] = {"sizehint", "flags", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:epoll", kwlist, - &sizehint)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:epoll", kwlist, + &sizehint, &flags)) + return NULL; + if (sizehint < 0) { + PyErr_SetString(PyExc_ValueError, "negative sizehint"); return NULL; + } - return newPyEpoll_Object(type, sizehint, -1); + return newPyEpoll_Object(type, sizehint, flags, -1); } @@ -792,7 +1150,7 @@ pyepoll_close(pyEpoll_Object *self) { errno = pyepoll_internal_close(self); if (errno < 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -834,7 +1192,7 @@ pyepoll_fromfd(PyObject *cls, PyObject *args) if (!PyArg_ParseTuple(args, "i:fromfd", &fd)) return NULL; - return newPyEpoll_Object((PyTypeObject*)cls, -1, fd); + return newPyEpoll_Object((PyTypeObject*)cls, FD_SETSIZE - 1, 0, fd); } PyDoc_STRVAR(pyepoll_fromfd_doc, @@ -885,7 +1243,7 @@ pyepoll_internal_ctl(int epfd, int op, PyObject *pfd, unsigned int events) } if (result < 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -909,7 +1267,7 @@ pyepoll_register(pyEpoll_Object *self, PyObject *args, PyObject *kwds) PyDoc_STRVAR(pyepoll_register_doc, "register(fd[, eventmask]) -> None\n\ \n\ -Registers a new fd or raises an IOError if the fd is already registered.\n\ +Registers a new fd or raises an OSError if the fd is already registered.\n\ fd is the target file descriptor of the operation.\n\ events is a bit set composed of the various EPOLL constants; the default\n\ is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\ @@ -1008,7 +1366,7 @@ pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds) nfds = epoll_wait(self->epfd, evs, maxevents, timeout); Py_END_ALLOW_THREADS if (nfds < 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); goto error; } @@ -1063,7 +1421,7 @@ static PyGetSetDef pyepoll_getsetlist[] = { }; PyDoc_STRVAR(pyepoll_doc, -"select.epoll([sizehint=-1])\n\ +"select.epoll(sizehint=-1, flags=0)\n\ \n\ Returns an epolling object\n\ \n\ @@ -1399,7 +1757,7 @@ newKqueue_Object(PyTypeObject *type, SOCKET fd) } if (self->kqfd < 0) { Py_DECREF(self); - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } return (PyObject *)self; @@ -1431,7 +1789,7 @@ kqueue_queue_close(kqueue_queue_Object *self) { errno = kqueue_queue_internal_close(self); if (errno < 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); return NULL; } Py_RETURN_NONE; @@ -1494,7 +1852,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) PyObject *result = NULL; struct kevent *evl = NULL; struct kevent *chl = NULL; - struct timespec timeoutspec; + struct timespec timeout; struct timespec *ptimeoutspec; if (self->kqfd < 0) @@ -1514,28 +1872,16 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) ptimeoutspec = NULL; } else if (PyNumber_Check(otimeout)) { - double timeout; - long seconds; - - timeout = PyFloat_AsDouble(otimeout); - if (timeout == -1 && PyErr_Occurred()) - return NULL; - if (timeout > (double)LONG_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout period too long"); + if (_PyTime_ObjectToTimespec(otimeout, + &timeout.tv_sec, &timeout.tv_nsec) == -1) return NULL; - } - if (timeout < 0) { + + if (timeout.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be positive or None"); return NULL; } - - seconds = (long)timeout; - timeout = timeout - (double)seconds; - timeoutspec.tv_sec = seconds; - timeoutspec.tv_nsec = (long)(timeout * 1E9); - ptimeoutspec = &timeoutspec; + ptimeoutspec = &timeout; } else { PyErr_Format(PyExc_TypeError, @@ -1712,6 +2058,11 @@ static PyTypeObject kqueue_queue_Type = { }; #endif /* HAVE_KQUEUE */ + + + + + /* ************************************************************************ */ PyDoc_STRVAR(select_doc, @@ -1743,6 +2094,9 @@ static PyMethodDef select_methods[] = { #ifdef HAVE_POLL {"poll", select_poll, METH_NOARGS, poll_doc}, #endif /* HAVE_POLL */ +#ifdef HAVE_SYS_DEVPOLL_H + {"devpoll", select_devpoll, METH_NOARGS, devpoll_doc}, +#endif {0, 0}, /* sentinel */ }; @@ -1765,6 +2119,9 @@ static struct PyModuleDef selectmodule = { NULL }; + + + PyMODINIT_FUNC PyInit_select(void) { @@ -1773,9 +2130,8 @@ PyInit_select(void) if (m == NULL) return NULL; - SelectError = PyErr_NewException("select.error", NULL, NULL); - Py_INCREF(SelectError); - PyModule_AddObject(m, "error", SelectError); + Py_INCREF(PyExc_OSError); + PyModule_AddObject(m, "error", PyExc_OSError); #ifdef PIPE_BUF #ifdef HAVE_BROKEN_PIPE_BUF @@ -1822,6 +2178,11 @@ PyInit_select(void) } #endif /* HAVE_POLL */ +#ifdef HAVE_SYS_DEVPOLL_H + if (PyType_Ready(&devpoll_Type) < 0) + return NULL; +#endif + #ifdef HAVE_EPOLL Py_TYPE(&pyEpoll_Type) = &PyType_Type; if (PyType_Ready(&pyEpoll_Type) < 0) @@ -1846,6 +2207,10 @@ PyInit_select(void) PyModule_AddIntConstant(m, "EPOLLWRNORM", EPOLLWRNORM); PyModule_AddIntConstant(m, "EPOLLWRBAND", EPOLLWRBAND); PyModule_AddIntConstant(m, "EPOLLMSG", EPOLLMSG); + +#ifdef EPOLL_CLOEXEC + PyModule_AddIntConstant(m, "EPOLL_CLOEXEC", EPOLL_CLOEXEC); +#endif #endif /* HAVE_EPOLL */ #ifdef HAVE_KQUEUE |