summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesus Cea <jcea@jcea.es>2011-11-14 18:07:41 (GMT)
committerJesus Cea <jcea@jcea.es>2011-11-14 18:07:41 (GMT)
commitd8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f (patch)
treec4b426e7c29b2547e789557cbfb649aacbab2cc8
parentd5d4406c8ebbbdf8a8961fc119be22b15a1c40ad (diff)
downloadcpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.zip
cpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.tar.gz
cpython-d8b9ae6e8f6d9a562ccdf4700d24c0155979fb4f.tar.bz2
Issue #6397: Support '/dev/poll' polling objects in select module, under Solaris & derivatives.
-rw-r--r--Doc/library/select.rst84
-rw-r--r--Lib/test/test_devpoll.py94
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/selectmodule.c370
-rwxr-xr-xconfigure7
-rw-r--r--configure.in7
-rw-r--r--pyconfig.h.in3
7 files changed, 561 insertions, 7 deletions
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index a4bc6fc..12b05d2 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -6,7 +6,8 @@
This module provides access to the :c:func:`select` and :c:func:`poll` functions
-available in most operating systems, :c:func:`epoll` available on Linux 2.5+ and
+available in most operating systems, :c:func:`devpoll` available on
+Solaris and derivatives, :c:func:`epoll` available on Linux 2.5+ and
:c:func:`kqueue` available on most BSD.
Note that on Windows, it only works for sockets; on other operating systems,
it also works for other file types (in particular, on Unix, it works on pipes).
@@ -24,6 +25,19 @@ The module defines the following:
Following :pep:`3151`, this class was made an alias of :exc:`OSError`.
+.. function:: devpoll()
+ (Only supported on Solaris and derivatives.) Returns a ``/dev/poll``
+ polling object; see section :ref:`devpoll-objects` below for the
+ methods supported by devpoll objects.
+
+ :c:func:`devpoll` objects are linked to the number of file
+ descriptors allowed at the time of instantiation. If your program
+ reduces this value, :c:func:`devpoll` will fail. If your program
+ increases this value, c:func:`devpoll` may return an
+ incomplete list of active file descriptors.
+
+ .. versionadded:: 3.3
+
.. function:: epoll(sizehint=-1)
(Only supported on Linux 2.5.44 and newer.) Returns an edge polling object,
@@ -107,6 +121,74 @@ The module defines the following:
.. versionadded:: 3.2
+.. _devpoll-objects:
+
+``/dev/poll`` Polling Objects
+----------------------------------------------
+
+ http://developers.sun.com/solaris/articles/using_devpoll.html
+ http://developers.sun.com/solaris/articles/polling_efficient.html
+
+Solaris and derivatives have ``/dev/poll``. While :c:func:`select` is
+O(highest file descriptor) and :c:func:`poll` is O(number of file
+descriptors), ``/dev/poll`` is O(active file descriptors).
+
+``/dev/poll`` behaviour is very close to the standard :c:func:`poll`
+object.
+
+
+.. method:: devpoll.register(fd[, eventmask])
+
+ Register a file descriptor with the polling object. Future calls to the
+ :meth:`poll` method will then check whether the file descriptor has any pending
+ I/O events. *fd* can be either an integer, or an object with a :meth:`fileno`
+ method that returns an integer. File objects implement :meth:`fileno`, so they
+ can also be used as the argument.
+
+ *eventmask* is an optional bitmask describing the type of events you want to
+ check for. The constants are the same that with :c:func:`poll`
+ object. The default value is a combination of the constants :const:`POLLIN`,
+ :const:`POLLPRI`, and :const:`POLLOUT`.
+
+ .. warning::
+
+ Registering a file descriptor that's already registered is not an
+ error, but the result is undefined. The appropiate action is to
+ unregister or modify it first. This is an important difference
+ compared with :c:func:`poll`.
+
+
+.. method:: devpoll.modify(fd[, eventmask])
+
+ This method does an :meth:`unregister` followed by a
+ :meth:`register`. It is (a bit) more efficient that doing the same
+ explicitly.
+
+
+.. method:: devpoll.unregister(fd)
+
+ Remove a file descriptor being tracked by a polling object. Just like the
+ :meth:`register` method, *fd* can be an integer or an object with a
+ :meth:`fileno` method that returns an integer.
+
+ Attempting to remove a file descriptor that was never registered is
+ safely ignored.
+
+
+.. method:: devpoll.poll([timeout])
+
+ Polls the set of registered file descriptors, and returns a possibly-empty list
+ containing ``(fd, event)`` 2-tuples for the descriptors that have events or
+ errors to report. *fd* is the file descriptor, and *event* is a bitmask with
+ bits set for the reported events for that descriptor --- :const:`POLLIN` for
+ waiting input, :const:`POLLOUT` to indicate that the descriptor can be written
+ to, and so forth. An empty list indicates that the call timed out and no file
+ descriptors had any events to report. If *timeout* is given, it specifies the
+ length of time in milliseconds which the system will wait for events before
+ returning. If *timeout* is omitted, -1, or :const:`None`, the call will
+ block until there is an event for this poll object.
+
+
.. _epoll-objects:
Edge and Level Trigger Polling (epoll) Objects
diff --git a/Lib/test/test_devpoll.py b/Lib/test/test_devpoll.py
new file mode 100644
index 0000000..bef4e18
--- /dev/null
+++ b/Lib/test/test_devpoll.py
@@ -0,0 +1,94 @@
+# Test case for the select.devpoll() function
+
+# Initial tests are copied as is from "test_poll.py"
+
+import os, select, random, unittest, sys
+from test.support import TESTFN, run_unittest
+
+try:
+ select.devpoll
+except AttributeError:
+ raise unittest.SkipTest("select.devpoll not defined -- skipping test_devpoll")
+
+
+def find_ready_matching(ready, flag):
+ match = []
+ for fd, mode in ready:
+ if mode & flag:
+ match.append(fd)
+ return match
+
+class DevPollTests(unittest.TestCase):
+
+ def test_devpoll1(self):
+ # Basic functional test of poll object
+ # Create a bunch of pipe and test that poll works with them.
+
+ p = select.devpoll()
+
+ NUM_PIPES = 12
+ MSG = b" This is a test."
+ MSG_LEN = len(MSG)
+ readers = []
+ writers = []
+ r2w = {}
+ w2r = {}
+
+ for i in range(NUM_PIPES):
+ rd, wr = os.pipe()
+ p.register(rd)
+ p.modify(rd, select.POLLIN)
+ p.register(wr, select.POLLOUT)
+ readers.append(rd)
+ writers.append(wr)
+ r2w[rd] = wr
+ w2r[wr] = rd
+
+ bufs = []
+
+ while writers:
+ ready = p.poll()
+ ready_writers = find_ready_matching(ready, select.POLLOUT)
+ if not ready_writers:
+ self.fail("no pipes ready for writing")
+ wr = random.choice(ready_writers)
+ os.write(wr, MSG)
+
+ ready = p.poll()
+ ready_readers = find_ready_matching(ready, select.POLLIN)
+ if not ready_readers:
+ self.fail("no pipes ready for reading")
+ self.assertEqual([w2r[wr]], ready_readers)
+ rd = ready_readers[0]
+ buf = os.read(rd, MSG_LEN)
+ self.assertEqual(len(buf), MSG_LEN)
+ bufs.append(buf)
+ os.close(r2w[rd]) ; os.close(rd)
+ p.unregister(r2w[rd])
+ p.unregister(rd)
+ writers.remove(r2w[rd])
+
+ self.assertEqual(bufs, [MSG] * NUM_PIPES)
+
+ def test_timeout_overflow(self):
+ pollster = select.devpoll()
+ w, r = os.pipe()
+ pollster.register(w)
+
+ pollster.poll(-1)
+ self.assertRaises(OverflowError, pollster.poll, -2)
+ self.assertRaises(OverflowError, pollster.poll, -1 << 31)
+ self.assertRaises(OverflowError, pollster.poll, -1 << 64)
+
+ pollster.poll(0)
+ pollster.poll(1)
+ pollster.poll(1 << 30)
+ self.assertRaises(OverflowError, pollster.poll, 1 << 31)
+ self.assertRaises(OverflowError, pollster.poll, 1 << 63)
+ self.assertRaises(OverflowError, pollster.poll, 1 << 64)
+
+def test_main():
+ run_unittest(DevPollTests)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index c462d6f..423e29d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -365,6 +365,9 @@ Core and Builtins
Library
-------
+- Issue #6397: Support "/dev/poll" polling objects in select module,
+ under Solaris & derivatives.
+
- Issues #1745761, #755670, #13357, #12629, #1200313: HTMLParser now correctly
handles non-valid attributes, including adjacent and unquoted attributes.
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)
diff --git a/configure b/configure
index 4812732..596336e 100755
--- a/configure
+++ b/configure
@@ -6139,12 +6139,13 @@ fi
for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \
fcntl.h grp.h \
-ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
+ieeefp.h io.h langinfo.h libintl.h ncurses.h process.h pthread.h \
sched.h shadow.h signal.h stdint.h stropts.h termios.h \
unistd.h utime.h \
-sys/audioio.h sys/xattr.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \
+poll.h sys/devpoll.h sys/epoll.h sys/poll.h \
+sys/audioio.h sys/xattr.h sys/bsdtty.h sys/event.h sys/file.h sys/loadavg.h \
sys/lock.h sys/mkdev.h sys/modem.h \
-sys/param.h sys/poll.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
+sys/param.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
sys/stat.h sys/termio.h sys/time.h \
sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
diff --git a/configure.in b/configure.in
index 7b0a88b..532ec92 100644
--- a/configure.in
+++ b/configure.in
@@ -1329,12 +1329,13 @@ dnl AC_MSG_RESULT($cpp_type)
AC_HEADER_STDC
AC_CHECK_HEADERS(asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \
fcntl.h grp.h \
-ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \
+ieeefp.h io.h langinfo.h libintl.h ncurses.h process.h pthread.h \
sched.h shadow.h signal.h stdint.h stropts.h termios.h \
unistd.h utime.h \
-sys/audioio.h sys/xattr.h sys/bsdtty.h sys/epoll.h sys/event.h sys/file.h sys/loadavg.h \
+poll.h sys/devpoll.h sys/epoll.h sys/poll.h \
+sys/audioio.h sys/xattr.h sys/bsdtty.h sys/event.h sys/file.h sys/loadavg.h \
sys/lock.h sys/mkdev.h sys/modem.h \
-sys/param.h sys/poll.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
+sys/param.h sys/select.h sys/sendfile.h sys/socket.h sys/statvfs.h \
sys/stat.h sys/termio.h sys/time.h \
sys/times.h sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h pty.h \
libutil.h sys/resource.h netpacket/packet.h sysexits.h bluetooth.h \
diff --git a/pyconfig.h.in b/pyconfig.h.in
index 33ba6c7..2d1fd48 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -883,6 +883,9 @@
/* Define to 1 if you have the <sys/bsdtty.h> header file. */
#undef HAVE_SYS_BSDTTY_H
+/* Define to 1 if you have the <sys/devpoll.h> header file. */
+#undef HAVE_SYS_DEVPOLL_H
+
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H