diff options
author | Jesus Cea <jcea@jcea.es> | 2011-11-14 18:07:41 (GMT) |
---|---|---|
committer | Jesus Cea <jcea@jcea.es> | 2011-11-14 18:07:41 (GMT) |
commit | d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f (patch) | |
tree | c4b426e7c29b2547e789557cbfb649aacbab2cc8 /Modules | |
parent | d5d4406c8ebbbdf8a8961fc119be22b15a1c40ad (diff) | |
download | cpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.zip cpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.tar.gz cpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.tar.bz2 |
Issue #6397: Support '/dev/poll' polling objects in select module, under Solaris & derivatives.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/selectmodule.c | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 6071144..5d5e772 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. @@ -648,6 +656,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."); @@ -658,6 +999,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 @@ -1715,6 +2069,11 @@ static PyTypeObject kqueue_queue_Type = { }; #endif /* HAVE_KQUEUE */ + + + + + /* ************************************************************************ */ PyDoc_STRVAR(select_doc, @@ -1746,6 +2105,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 */ }; @@ -1768,6 +2130,9 @@ static struct PyModuleDef selectmodule = { NULL }; + + + PyMODINIT_FUNC PyInit_select(void) { @@ -1824,6 +2189,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) |