Issue #6397: Support '/dev/poll' polling objects in select module, under Solaris & derivatives.

This commit is contained in:
Jesus Cea 2011-11-14 19:07:41 +01:00
parent d5d4406c8e
commit d8b9ae6e8f
7 changed files with 561 additions and 7 deletions

View file

@ -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)