mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
Issue #6397: Support '/dev/poll' polling objects in select module, under Solaris & derivatives.
This commit is contained in:
parent
d5d4406c8e
commit
d8b9ae6e8f
7 changed files with 561 additions and 7 deletions
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue