Index: configure =================================================================== --- configure (Revision 59566) +++ configure (Arbeitskopie) @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.in Revision: 59533 . +# From configure.in Revision: 59558 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.61 for python 2.6. # @@ -5416,13 +5416,15 @@ + + for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ shadow.h signal.h stdint.h stropts.h termios.h thread.h \ unistd.h utime.h \ -sys/audioio.h sys/bsdtty.h sys/file.h sys/loadavg.h sys/lock.h sys/mkdev.h \ -sys/modem.h \ +sys/audioio.h sys/bsdtty.h sys/epoll.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/socket.h sys/statvfs.h sys/stat.h \ sys/time.h \ sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \ @@ -15644,7 +15646,111 @@ fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: checking for epoll" >&5 +echo $ECHO_N "checking for epoll... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +void *x=epoll_create + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then +cat >>confdefs.h <<\_ACEOF +#define HAVE_EPOLL 1 +_ACEOF + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: checking for kqueue" >&5 +echo $ECHO_N "checking for kqueue... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#include + +int +main () +{ +int x=kqueue() + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_KQUEUE 1 +_ACEOF + + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # On some systems (eg. FreeBSD 5), we would find a definition of the # functions ctermid_r, setgroups in the library, but no prototype # (e.g. because we use _XOPEN_SOURCE). See whether we can take their Index: configure.in =================================================================== --- configure.in (Revision 59566) +++ configure.in (Arbeitskopie) @@ -1100,8 +1100,8 @@ io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ shadow.h signal.h stdint.h stropts.h termios.h thread.h \ unistd.h utime.h \ -sys/audioio.h sys/bsdtty.h sys/file.h sys/loadavg.h sys/lock.h sys/mkdev.h \ -sys/modem.h \ +sys/audioio.h sys/bsdtty.h sys/epoll.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/socket.h sys/statvfs.h sys/stat.h \ sys/time.h \ sys/times.h sys/types.h sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h \ @@ -2358,7 +2358,21 @@ AC_MSG_RESULT(yes), AC_MSG_RESULT(no) ) - +AC_MSG_CHECKING(for epoll) +AC_TRY_COMPILE([#include ], void *x=epoll_create, + AC_DEFINE(HAVE_EPOLL, 1, Define if you have the 'epoll' functions.) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) +AC_MSG_CHECKING(for kqueue) +AC_TRY_COMPILE([ +#include +#include + ], int x=kqueue(), + AC_DEFINE(HAVE_KQUEUE, 1, Define if you have the 'kqueue' functions.) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) # On some systems (eg. FreeBSD 5), we would find a definition of the # functions ctermid_r, setgroups in the library, but no prototype # (e.g. because we use _XOPEN_SOURCE). See whether we can take their Index: Doc/license.rst =================================================================== --- Doc/license.rst (Revision 59566) +++ Doc/license.rst (Arbeitskopie) @@ -645,3 +645,58 @@ ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +test_epoll +---------- + +The :mod:`test_epoll` contains the following notice:: + + Copyright (c) 2001-2006 Twisted Matrix Laboratories. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Select kqueue +------------- + +The :mod:`select` and contains the following notice for the kqueue interface:: + + Copyright (c) 2000 Doug White, 2006 James Knight + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. Index: Doc/library/select.rst =================================================================== --- Doc/library/select.rst (Revision 59566) +++ Doc/library/select.rst (Arbeitskopie) @@ -7,10 +7,11 @@ This module provides access to the :cfunc:`select` and :cfunc:`poll` functions -available in most operating systems. 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). It cannot be used on regular files to -determine whether a file has grown since it was last read. +available in most operating systems and :cfunc:`epoll` available on Linux 2.5+. +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). +It cannot be used on regular files to determine whether a file has grown since +it was last read. The module defines the following: @@ -22,6 +23,16 @@ string, as would be printed by the C function :cfunc:`perror`. +.. function:: epoll(sizehint) + + (Only supported on Linux 2.5.44 and newer.) Returns an edge polling + object, which can be used as Edge or Level Triggered interface for I/O + events; see section :ref:`epoll-objects` below for the methods supported + by epolling objects. + + .. versionadded:: 2.6 + + .. function:: poll() (Not supported by all operating systems.) Returns a polling object, which @@ -69,6 +80,74 @@ not handle file descriptors that don't originate from WinSock. +.. _epoll-objects: + +Edge and Level Trigger Polling (epoll) Objects +---------------------------------------------- + + *eventmask* + + +------------------------+-----------------------------------------------+ + | Constant | Meaning | + +========================+===============================================+ + | :const:`EPOLL_IN` | Available for read | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_OUT` | Available for write | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_PRI` | Urgent data for read | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_ERR` | Error condition happend on the assoc. fd | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_HUP` | Hang up happend on the assoc. fd | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_ET` | Set Edge Trigger behavior, the default is | + | | Level Trigger behavior + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_ONESHOT` | Set one-shot behavior. After one event is | + | | pulled out, the fd is internally disabled | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_RDNORM` | ??? | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_RDBAND` | ??? | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_WRNORM` | ??? | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_WRBAND` | ??? | + +------------------------+-----------------------------------------------+ + | :const:`EPOLL_MSG` | ??? | + +------------------------+-----------------------------------------------+ + + +.. method:: epoll.close() + + Close the control file descriptor of the epoll object. + + +.. method:: epoll.fileno() + + Return the file descriptor number of the control fd. + + +.. method:: epoll.register(fd[, eventmask]) + + Register a fd descriptor with the epoll object. + + +.. method:: epoll.modify(fd, eventmask) + + Modify a register file descriptor. + + +.. method:: epoll.unregister(fd) + + Remove a registered file descriptor from the epoll object. + + +.. method:: epoll.wait(maxevents, timeout) + + Wait for events. + + .. _poll-objects: Polling Objects Index: Lib/test/test_kqueue.py =================================================================== --- Lib/test/test_kqueue.py (Revision 0) +++ Lib/test/test_kqueue.py (Revision 0) @@ -0,0 +1,78 @@ +""" +Tests for kqeue wrapper. +""" +import socket, errno, time, select, sys +import unittest + +class TestKQueue(unittest.TestCase): + def test_create_queue(self): + kq = select.kqueue() + self.assert_(kq.fileno() > 0, kq.fileno()) + self.assert_(not kq.closed) + kq.close() + self.assert_(kq.closed) + self.assertRaises(ValueError, kq.fileno) + + def test_create_event(self): + fd = sys.stderr.fileno() + ev = select.kevent(fd) + self.assertEqual(ev.ident, fd) + self.assertEqual(ev.filter, select.KQE_FILTER_READ) + self.assertEqual(ev.flags, select.KQE_ADD) + self.assertEqual(ev.fflags, 0) + self.assertEqual(ev.data, 0) + self.assertEqual(ev.udata, 0) + + ev = select.kevent(fd, select.KQE_FILTER_WRITE) + self.assertEqual(ev.ident, fd) + self.assertEqual(ev.filter, select.KQE_FILTER_WRITE) + self.assertEqual(ev.flags, select.KQE_ADD) + self.assertEqual(ev.fflags, 0) + self.assertEqual(ev.data, 0) + self.assertEqual(ev.udata, 0) + + ev = select.kevent(fd, select.KQE_FILTER_WRITE, select.KQE_ONESHOT) + self.assertEqual(ev.ident, fd) + self.assertEqual(ev.filter, select.KQE_FILTER_WRITE) + self.assertEqual(ev.flags, select.KQE_ONESHOT) + self.assertEqual(ev.fflags, 0) + self.assertEqual(ev.data, 0) + self.assertEqual(ev.udata, 0) + + ev = select.kevent(1, 2, 3, 4, 5, 6) + self.assertEqual(ev.ident, 1) + self.assertEqual(ev.filter, 2) + self.assertEqual(ev.flags, 3) + self.assertEqual(ev.fflags, 4) + self.assertEqual(ev.data, 5) + self.assertEqual(ev.udata, 6) + + def test_queue_event(self): + fd = sys.stderr.fileno() + ev = select.kevent(fd, select.KQE_FILTER_WRITE, + select.KQE_ONESHOT|select.KQE_ADD) + kq = select.kqueue() + kq.control([ev], 0, 0) + + before = time.time() + kq.control([], 1, None) + after = time.time() + self.assert_(before - after < 0.1) + +if not hasattr(select, "kqueue") is None: + TestKQueue.skip = "select.kqueue unavailable" +else: + try: + e = select.kqueue() + except IOError, exc: + if exc.errno == errno.ENOSYS: + del exc + TestKQueue.skip = "kqueue support missing from platform" + else: + raise + else: + e.close() + del e + +if __name__ == '__main__': + unittest.main() Eigenschaftsänderungen: Lib/test/test_kqueue.py ___________________________________________________________________ Name: svn:keywords + 'Id Revision' Name: svn:eol-style + native Index: Lib/test/test_epoll.py =================================================================== --- Lib/test/test_epoll.py (Revision 0) +++ Lib/test/test_epoll.py (Revision 0) @@ -0,0 +1,191 @@ +# Copyright (c) 2001-2006 Twisted Matrix Laboratories. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +Tests for epoll wrapper. +""" +import socket, errno, time, select +import unittest + +class EPoll(unittest.TestCase): + """ + Tests for the low-level epoll bindings. + """ + def setUp(self): + """ + Create a listening server port and a list with which to keep track + of created sockets. + """ + self.serverSocket = socket.socket() + self.serverSocket.bind(('127.0.0.1', 0)) + self.serverSocket.listen(1) + self.connections = [self.serverSocket] + + + def tearDown(self): + """ + Close any sockets which were opened by the test. + """ + for skt in self.connections: + skt.close() + + + def _connected_pair(self): + """ + Return the two sockets which make up a new TCP connection. + """ + client = socket.socket() + client.setblocking(False) + try: + client.connect(('127.0.0.1', self.serverSocket.getsockname()[1])) + except socket.error, e: + self.assertEquals(e.args[0], errno.EINPROGRESS) + else: + raise AssertionError("Connect should have raised EINPROGRESS") + server, addr = self.serverSocket.accept() + + self.connections.extend((client, server)) + return client, server + + + def test_create(self): + """ + Test the creation of an epoll object. + """ + try: + ep = select.epoll(16) + except OSError, e: + raise AssertionError(str(e)) + self.assert_(ep.fileno() > 0, ep.fileno()) + self.assert_(not ep.closed) + ep.close() + self.assert_(ep.closed) + self.assertRaises(ValueError, ep.fileno) + + def test_badcreate(self): + """ + Test that attempting to create an epoll object with some random + objects raises a TypeError. + """ + self.assertRaises(TypeError, select.epoll, 1, 2, 3) + self.assertRaises(TypeError, select.epoll, 'foo') + self.assertRaises(TypeError, select.epoll, None) + self.assertRaises(TypeError, select.epoll, ()) + self.assertRaises(TypeError, select.epoll, ['foo']) + self.assertRaises(TypeError, select.epoll, {}) + self.assertRaises(TypeError, select.epoll) + + + def test_add(self): + """ + Test adding a socket to an epoll object. + """ + server, client = self._connected_pair() + + ep = select.epoll(2) + try: + ep.register(server.fileno(), select.EPOLL_IN | select.EPOLL_OUT) + ep.register(client.fileno(), select.EPOLL_IN | select.EPOLL_OUT) + finally: + ep.close() + + + def test_control_and_wait(self): + """ + Test waiting on an epoll object which has had some sockets added to + it. + """ + client, server = self._connected_pair() + + ep = select.epoll(16) + ep.register(server.fileno(), + select.EPOLL_IN | select.EPOLL_OUT | select.EPOLL_ET) + ep.register(client.fileno(), + select.EPOLL_IN | select.EPOLL_OUT | select.EPOLL_ET) + + now = time.time() + events = ep.wait(4, 1000) + then = time.time() + self.failIf(then - now > 0.1, then - now) + + events.sort() + expected = [(client.fileno(), select.EPOLL_OUT), + (server.fileno(), select.EPOLL_OUT)] + expected.sort() + + self.assertEquals(events, expected) + self.failIf(then - now > 0.01, then - now) + + now = time.time() + events = ep.wait(4, 200) + then = time.time() + self.failIf(events) + + client.send("Hello!") + server.send("world!!!") + + now = time.time() + events = ep.wait(4, 1000) + then = time.time() + self.failIf(then - now > 0.01) + + events.sort() + expected = [(client.fileno(), select.EPOLL_IN | select.EPOLL_OUT), + (server.fileno(), select.EPOLL_IN | select.EPOLL_OUT)] + expected.sort() + + self.assertEquals(events, expected) + + ep.unregister(client.fileno()) + ep.modify(server.fileno(), select.EPOLL_OUT) + now = time.time() + events = ep.wait(4, 1000) + then = time.time() + self.failIf(then - now > 0.01) + + expected = [(server.fileno(), select.EPOLL_OUT)] + self.assertEquals(events, expected) + + + def test_errors(self): + """ + Test some error conditions of epoll methods. + """ + self.assertRaises(IOError, select.epoll, -1) + self.assertRaises(IOError, select.epoll(1).register, -1, + select.EPOLL_IN) + +if not hasattr(select, "epoll") is None: + EPoll.skip = "select.epoll unavailable" +else: + try: + e = select.epoll(16) + except IOError, exc: + if exc.errno == errno.ENOSYS: + del exc + EPoll.skip = "epoll support missing from platform" + else: + raise + else: + e.close() + del e + +if __name__ == '__main__': + unittest.main() Eigenschaftsänderungen: Lib/test/test_epoll.py ___________________________________________________________________ Name: svn:keywords + 'Id Revision' Name: svn:eol-style + native Index: Modules/selectmodule.c =================================================================== --- Modules/selectmodule.c (Revision 59566) +++ Modules/selectmodule.c (Arbeitskopie) @@ -7,6 +7,7 @@ */ #include "Python.h" +#include #ifdef __APPLE__ /* Perform runtime testing for a broken poll on OSX to make it easier @@ -483,9 +484,9 @@ return NULL; /* call poll() */ - Py_BEGIN_ALLOW_THREADS; + Py_BEGIN_ALLOW_THREADS poll_result = poll(self->ufds, self->ufd_len, timeout); - Py_END_ALLOW_THREADS; + Py_END_ALLOW_THREADS if (poll_result < 0) { PyErr_SetFromErrno(SelectError); @@ -646,6 +647,804 @@ #endif /* HAVE_POLL */ +#ifdef HAVE_EPOLL +/* ************************************************************************** + * epoll interface for Linux 2.6 + * + * Written by Christian Heimes + * Inspired by Twisted's _epoll.pyx and select.poll() + */ + +#ifdef HAVE_SYS_EPOLL_H +#include +#endif + +typedef struct { + PyObject_HEAD + int epfd; +} PyEpoll_Object; + +static PyTypeObject PyEpoll_Type; +#define PyEpoll_CHECK(op) (PyObject_TypeCheck((op), &PyEpoll_Type)) + +static PyObject * +pyepoll_err_closed(void) +{ + PyErr_SetString(PyExc_ValueError, "I/O operation on closed epoll fd"); + return NULL; +} + +static int +pyepoll_internal_close(PyEpoll_Object *self) +{ + int save_errno = 0; + if (self->epfd >= 0) { + int epfd = self->epfd; + self->epfd = -1; + Py_BEGIN_ALLOW_THREADS + if (close(epfd) < 0) + save_errno = errno; + Py_END_ALLOW_THREADS + } + return save_errno; +} + +static PyObject * +pyepoll_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyEpoll_Object *self; + int size; + static char *kwlist[] = {"size", NULL}; + + assert(type != NULL && type->tp_alloc != NULL); + + self = (PyEpoll_Object *) type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:epoll", kwlist, &size)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + self->epfd = epoll_create(size); + Py_END_ALLOW_THREADS + if (self->epfd < 0) { + Py_DECREF(self); + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return (PyObject *)self; +} + +static void +pyepoll_dealloc(PyEpoll_Object *self) +{ + (void)pyepoll_internal_close(self); +} + +static PyObject* +pyepoll_close(PyEpoll_Object *self) +{ + errno = pyepoll_internal_close(self); + if (errno < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pyepoll_close_doc, +"close() -> None\n\ +\n\ +Close the epoll control file descriptor. Further operations on the epoll\n\ +object will raise an exception."); + +static PyObject* +pyepoll_get_closed(PyEpoll_Object *self) +{ + if (self->epfd < 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +static PyObject* +pyepoll_fileno(PyEpoll_Object *self) +{ + if (self->epfd < 0) + return pyepoll_err_closed(); + return PyInt_FromLong(self->epfd); +} + +PyDoc_STRVAR(pyepoll_fileno_doc, +"fileno() -> int\n\ +\n\ +Return the epoll control file descriptor."); + +static PyObject * +pyepoll_internal_ctl(int epfd, int op, int fd, unsigned int events) +{ + struct epoll_event ev; + int result; + + if (epfd < 0) + return pyepoll_err_closed(); + + switch(op) { + case EPOLL_CTL_ADD: + case EPOLL_CTL_MOD: + ev.events = events; + ev.data.fd = fd; + Py_BEGIN_ALLOW_THREADS + result = epoll_ctl(epfd, op, fd, &ev); + Py_END_ALLOW_THREADS + break; + case EPOLL_CTL_DEL: + /* In kernel versions before 2.6.9, the EPOLL_CTL_DEL + * operation required a non-NULL pointer in event, even + * though this argument is ignored. */ + Py_BEGIN_ALLOW_THREADS + result = epoll_ctl(epfd, op, fd, &ev); + Py_END_ALLOW_THREADS + break; + default: + result = -1; + errno = EINVAL; + } + + if (result < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pyepoll_register(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int fd; + unsigned int events = EPOLLIN | EPOLLOUT | EPOLLPRI; + static char *kwlist[] = {"fd", "eventmask", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|I:control", kwlist, + &fd, &events)) + return NULL; + return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_ADD, fd, events); +} + +PyDoc_STRVAR(pyepoll_register_doc, +"register(fd[, eventmask]) -> None\n\ +\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\ +\n\ +The epoll interface supports all file descriptors that support poll."); + +static PyObject * +pyepoll_modify(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int fd; + unsigned int events; + static char *kwlist[] = {"fd", "eventmask", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iI:control", kwlist, + &fd, &events)) + return NULL; + return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_MOD, fd, events); +} + +PyDoc_STRVAR(pyepoll_modify_doc, +"modify(fd, eventmask) -> None\n\ +\n\ +fd is the target file descriptor of the operation\n\ +events is a bit set composed of the various EPOLL constants"); + +static PyObject * +pyepoll_unregister(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int fd; + static char *kwlist[] = {"fd", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:control", kwlist, + &fd)) + return NULL; + return pyepoll_internal_ctl(self->epfd, EPOLL_CTL_DEL, fd, 0); +} + +PyDoc_STRVAR(pyepoll_unregister_doc, +"unregister(fd) -> None\n\ +\n\ +fd is the target file descriptor of the operation."); + +static PyObject * +pyepoll_wait(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int timeout, nfds, maxevents, size, i; + struct epoll_event *evs; + PyObject *elist = NULL, *etuple = NULL; + static char *kwlist[] = {"maxevents", "timeout", NULL}; + + if (self->epfd < 0) + return pyepoll_err_closed(); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii:wait", kwlist, + &maxevents, &timeout)) + return NULL; + + size = maxevents * sizeof(struct epoll_event); + evs = (struct epoll_event*) PyMem_Malloc(size); + if (evs == NULL) { + PyErr_SetString(PyExc_MemoryError, + "failed to allocate memory for events"); + return NULL; + } + memset(evs, 0, size); + + Py_BEGIN_ALLOW_THREADS + nfds = epoll_wait(self->epfd, evs, maxevents, timeout); + Py_END_ALLOW_THREADS + if (nfds < 0) { + PyErr_SetFromErrno(PyExc_IOError); + goto error; + } + + elist = PyList_New(nfds); + if (elist == NULL) { + goto error; + } + + for (i = 0; i < nfds; i++) { + etuple = Py_BuildValue("iI", evs[i].data.fd, evs[i].events); + if (etuple == NULL) { + goto error; + } + PyList_SET_ITEM(elist, i, etuple); + } + + if (0) { + error: + Py_CLEAR(elist); + Py_XDECREF(etuple); + } + PyMem_Free(evs); + return elist; +} + +PyDoc_STRVAR(pyepoll_wait_doc, +"wait(maxevents, timeout) -> [(fd, events), (...)]\n\ +\n\ +Wait for events on the epoll file descriptor for a maximum time of timeout\n\ +milliseconds. Up to maxevents are returned to the caller."); + +static PyMethodDef pyepoll_methods[] = { + {"close", (PyCFunction)pyepoll_close, METH_NOARGS, + pyepoll_close_doc}, + {"fileno", (PyCFunction)pyepoll_fileno, METH_NOARGS, + pyepoll_fileno_doc}, + {"modify", (PyCFunction)pyepoll_modify, + METH_VARARGS | METH_KEYWORDS, pyepoll_modify_doc}, + {"register", (PyCFunction)pyepoll_register, + METH_VARARGS | METH_KEYWORDS, pyepoll_register_doc}, + {"unregister", (PyCFunction)pyepoll_unregister, + METH_VARARGS | METH_KEYWORDS, pyepoll_unregister_doc}, + {"wait", (PyCFunction)pyepoll_wait, + METH_VARARGS | METH_KEYWORDS, pyepoll_wait_doc}, + {NULL, NULL}, +}; + +static PyGetSetDef pyepoll_getsetlist[] = { + {"closed", (getter)pyepoll_get_closed, NULL, + "True if the epoll handler is closed"}, + {0}, +}; + +PyDoc_STRVAR(pyepoll_doc, +"select.epoll(sizehint)\n\ +\n\ +Returns an epolling object"); + +static PyTypeObject PyEpoll_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "select.epoll", /* tp_name */ + sizeof(PyEpoll_Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)pyepoll_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + pyepoll_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + pyepoll_methods, /* tp_methods */ + 0, /* tp_members */ + pyepoll_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pyepoll_new, /* tp_new */ + 0, /* tp_free */ +}; + +#endif /* HAVE_EPOLL */ + +#ifdef HAVE_KQUEUE +/* ************************************************************************** + * kqueue interface for BSD + * + * Copyright (c) 2000 Doug White, 2006 James Knight + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * With some modifications from Christian Heimes + * + */ + +#ifdef HAVE_SYS_EVENT_H +#include +#endif + +PyDoc_STRVAR(kqueue_event_doc, +"Event(ident, filter, flags, fflags=0, data=0, udata=0)\n\ +\n\ +This object is the equivalent of the struct kevent for the C API.\n\ +\n\ +See the kqueue manpage for more detailed information about the meaning\n\ +of the arguments.\n\ +\n\ +One minor note: while you might hope that udata could store a\n\ +reference to a python object, it cannot, because it is impossible to\n\ +keep a proper reference count of the object once it's passed into the\n\ +kernel. Therefore, I have restricted it to only storing an integer. I\n\ +recommend ignoring it and simply using the 'ident' field to key off\n\ +of. You could also set up a dictionary on the python side to store a\n\ +udata->object mapping."); + +typedef struct { + PyObject_HEAD + struct kevent e; +} kqueue_event_Object; + +static PyTypeObject kqueue_event_Type; + +#define kqueue_event_Check(v) ((v)->ob_type == &kqueue_event_Type) + +typedef struct { + PyObject_HEAD + int kqfd; +} kqueue_queue_Object; + +static PyTypeObject kqueue_queue_Type; + +#define kqueue_queue_Check(op) (PyObject_TypeCheck((op), &kqueue_queue_Type)) + +#define KQ_OFF(x) offsetof(kqueue_event_Object, x) + +// struct kevent { +// uintptr_t ident; /* identifier for this event */ +// short filter; /* filter for event */ +// u_short flags; /* action flags for kqueue */ +// u_int fflags; /* filter flag value */ +// intptr_t data; /* filter data value */ +// void *udata; /* opaque user data identifier */ +// }; + +// Unfortunately, we can't store python objects in udata, because +// kevents in the kernel can be removed without warning, which would +// forever lose the refcount on the object stored with it. + +static struct PyMemberDef kqueue_event_members[] = { + {"ident", T_UINT, KQ_OFF(e.ident)}, + {"filter", T_SHORT, KQ_OFF(e.filter)}, + {"flags", T_USHORT, KQ_OFF(e.flags)}, + {"fflags", T_UINT, KQ_OFF(e.fflags)}, + {"data", T_INT, KQ_OFF(e.data)}, + {"udata", T_INT, KQ_OFF(e.udata)}, + {NULL} /* Sentinel */ +}; +#undef KQ_OFF + +static PyObject * +kqueue_event_repr(kqueue_event_Object *s) +{ + char buf[1024]; + PyOS_snprintf( + buf, sizeof(buf), + "", + s->e.ident, s->e.filter, s->e.flags, s->e.fflags, + s->e.data, s->e.udata + ); + return PyString_FromString(buf); +} + +static int +kqueue_event_init(kqueue_event_Object *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"ident", "filter", "flags", "fflags", + "data", "udata", NULL}; + + // defaults + self->e.ident = 0; + self->e.filter = EVFILT_READ; + self->e.flags = EV_ADD; + self->e.fflags = 0; + self->e.data = 0; + self->e.udata = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|hhiii:kevent", kwlist, + &(self->e.ident), &(self->e.filter), &(self->e.flags), + &(self->e.fflags), &(self->e.data), &(self->e.udata))) + return -1; + return 0; +} + +static PyTypeObject kqueue_event_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "select.kevent", /* tp_name */ + sizeof(kqueue_event_Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)kqueue_event_repr, /* 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 */ + kqueue_event_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + kqueue_event_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)kqueue_event_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ +}; + +static PyObject * +kqueue_queue_err_closed(void) +{ + PyErr_SetString(PyExc_ValueError, "I/O operation on closed kqueue fd"); + return NULL; +} + +static int +kqueue_queue_internal_close(kqueue_queue_Object *self) +{ + int save_errno = 0; + if (self->kqfd >= 0) { + int kqfd = self->kqfd; + self->kqfd = -1; + Py_BEGIN_ALLOW_THREADS + if (close(kqfd) < 0) + save_errno = errno; + Py_END_ALLOW_THREADS + } + return save_errno; +} + +static PyObject * +kqueue_queue_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + kqueue_queue_Object *self; + + assert(type != NULL && type->tp_alloc != NULL); + + self = (kqueue_queue_Object *) type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + if ((args != NULL && PyObject_Size(args)) || + (kwds != NULL && PyObject_Size(kwds))) { + PyErr_SetString(PyExc_ValueError, + "select.kqueue doesn't accept arguments"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + self->kqfd = kqueue(); + Py_END_ALLOW_THREADS + if (self->kqfd < 0) { + Py_DECREF(self); + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return (PyObject *)self; +} + +static void +kqueue_queue_dealloc(kqueue_queue_Object *self) +{ + (void)kqueue_queue_internal_close(self); +} + +static PyObject* +kqueue_queue_close(kqueue_queue_Object *self) +{ + errno = kqueue_queue_internal_close(self); + if (errno < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(kqueue_queue_close_doc, +"close() -> None\n\ +\n\ +Close the kqueue control file descriptor. Further operations on the kqueue\n\ +object will raise an exception."); + +static PyObject* +kqueue_queue_get_closed(kqueue_queue_Object *self) +{ + if (self->kqfd < 0) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + +static PyObject* +kqueue_queue_fileno(kqueue_queue_Object *self) +{ + if (self->kqfd < 0) + return kqueue_queue_err_closed(); + return PyInt_FromLong(self->kqfd); +} + +PyDoc_STRVAR(kqueue_queue_fileno_doc, +"fileno() -> int\n\ +\n\ +Return the kqueue control file descriptor."); + +static PyObject * +kqueue_queue_ctl(int kqfd, PyObject *kelist, int wantNumEvents, + PyObject *timeout) +{ + int gotNumEvents = 0; + int haveNumEvents = 0; + int i = 0; + int size; + PyObject *output = NULL; + struct kevent *eventlist; + struct timespec totimespec; + struct timespec *timeoutspec_p; + + if (kqfd < 0) + return kqueue_queue_err_closed(); + + assert(PyList_CheckExact(kelist)); + + if (timeout == Py_None) + timeoutspec_p = NULL; + else { + assert(PyInt_CheckExact(timeout) || PyLong_CheckExact(timeout)); + PY_LONG_LONG timeout_int = PyLong_AsLongLong(timeout); + if (timeout_int == (PY_LONG_LONG)-1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "timeout argument must be an integer or None"); + return NULL; + } + + /* Build timespec for timeout */ + totimespec.tv_sec = timeout_int / 1000000000; + totimespec.tv_nsec = (timeout_int % 1000000000); + timeoutspec_p = &totimespec; + } + + haveNumEvents = PyList_Size(kelist); +#define max(a,b) (((a) > (b)) ? (a) : (b)) + size = max(haveNumEvents, wantNumEvents) * sizeof(struct kevent); +#undef max + if (!(eventlist = PyMem_Malloc(size))) { + PyErr_SetString(PyExc_MemoryError, + "Failed to allocate memory for kevents"); + return NULL; + } + + for (i=0; i < haveNumEvents; i++) { + PyObject * ei = PyList_GET_ITEM(kelist, i); + + if (!kqueue_event_Check(ei)) { + PyErr_SetString(PyExc_TypeError, + "arg 1 must be a list of select.KEvent objects"); + goto post_eventlist_error; + } else { + /* copy this kevent into the array */ + eventlist[i] = ((kqueue_event_Object *)ei)->e; + } + } + + // printf("timespec: sec=%d nsec=%d\n", totimespec.tv_sec, totimespec.tv_nsec); + + /* Make the call, allowing other threads to run. */ + Py_BEGIN_ALLOW_THREADS + gotNumEvents = kevent(kqfd, eventlist, haveNumEvents, eventlist, + wantNumEvents, timeoutspec_p); + Py_END_ALLOW_THREADS + + if (gotNumEvents == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto post_eventlist_error; + } + + if ((output = PyList_New(gotNumEvents)) == NULL) { + goto post_eventlist_error; + } + + for (i=0; i < gotNumEvents; i++) { + kqueue_event_Object *ke; + + ke = PyObject_New(kqueue_event_Object, &kqueue_event_Type); + if (ke == NULL) { + goto post_output_error; + } + // copy event data into our struct + ke->e = eventlist[i]; + PyList_SET_ITEM(output, i, (PyObject *)ke); + } + + /* pass back the results */ + PyMem_Free(eventlist); + return output; + + post_output_error: + Py_DECREF(output); + post_eventlist_error: + PyMem_Free(eventlist); + return NULL; +} + +/* Module functions */ + +PyDoc_STRVAR(kqueue_queue_control_doc, +"kevent(changelist, max_events, timeout) -> eventlist\n\ +\n\ +Calls the kernel kevent function.\n\ +- changelist should be a list of Event objects describing the changes\n\ + to be made to the kernel's watch list.\n\ +- max_events lets you specify the maximum number of events that the\n\ + kernel will return.\n\ +- timeout is the maximum time to wait, in nanoseconds, or else None,\n\ + to wait forever."); + +static PyObject * +kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) +{ + int wantNumEvents = 0; + PyObject *timeout = NULL; + PyObject *kelist = NULL; + + if (!PyArg_ParseTuple(args, "O!iO", &PyList_Type, &kelist, + &wantNumEvents, &timeout)) { + return NULL; + } + return kqueue_queue_ctl(self->kqfd, kelist, wantNumEvents, timeout); +} + +static PyMethodDef kqueue_queue_methods[] = { + {"close", (PyCFunction)kqueue_queue_close, METH_NOARGS, + kqueue_queue_close_doc}, + {"fileno", (PyCFunction)kqueue_queue_fileno, METH_NOARGS, + kqueue_queue_fileno_doc}, + {"control", (PyCFunction)kqueue_queue_control, + METH_VARARGS , kqueue_queue_control_doc}, + {NULL, NULL}, +}; + +static PyGetSetDef kqueue_queue_getsetlist[] = { + {"closed", (getter)kqueue_queue_get_closed, NULL, + "True if the kqueue handler is closed"}, + {0}, +}; + +PyDoc_STRVAR(kqueue_queue_doc, +"Test"); + +static PyTypeObject kqueue_queue_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "select.kqueue", /* tp_name */ + sizeof(kqueue_queue_Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)kqueue_queue_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 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 */ + kqueue_queue_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + kqueue_queue_methods, /* tp_methods */ + 0, /* tp_members */ + kqueue_queue_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + kqueue_queue_new, /* tp_new */ + 0, /* tp_free */ +}; + +#endif /* HAVE_KQUEUE */ + PyDoc_STRVAR(select_doc, "select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)\n\ \n\ @@ -667,14 +1466,31 @@ that are ready.\n\ \n\ *** IMPORTANT NOTICE ***\n\ -On Windows and OpenVMS, only sockets are supported; on Unix, all file descriptors."); +On Windows and OpenVMS, only sockets are supported; on Unix, all file\n\ +descriptors." +#ifdef HAVE_KQUEUE +"\n\n\ +Kqueue syscall wrapper.\n\ +\n\ +For example, to start watching stdin for input, without returning any events:\n\ +>>> fd = kqueue()\n\ +>>> stdinfd = sys.stdin.fileno()\n\ +>>> kevent(fd, [Event(stdinfd, EVFILT_READ, EV_ADD)], 0, 0)\n\ +\n\ +To wait for it to become readable:\n\ +>>> kevent(fd, [], 1, None)\n\ +\n\ +To stop listening:\n\ +>>> kevent(fd, [Event(stdinfd, EVFILT_READ, EV_DELETE)], 0, 0)" +#endif /* HAVE_KQUEUE */ +); static PyMethodDef select_methods[] = { - {"select", select_select, METH_VARARGS, select_doc}, -#if defined(HAVE_POLL) - {"poll", select_poll, METH_NOARGS, poll_doc}, + {"select", select_select, METH_VARARGS, select_doc}, +#ifdef HAVE_POLL + {"poll", select_poll, METH_NOARGS, poll_doc}, #endif /* HAVE_POLL */ - {0, 0}, /* sentinel */ + {0, 0}, /* sentinel */ }; PyDoc_STRVAR(module_doc, @@ -694,8 +1510,8 @@ SelectError = PyErr_NewException("select.error", NULL, NULL); Py_INCREF(SelectError); PyModule_AddObject(m, "error", SelectError); -#if defined(HAVE_POLL) +#if defined(HAVE_POLL) #ifdef __APPLE__ if (select_have_broken_poll()) { if (PyObject_DelAttrString(m, "poll") == -1) { @@ -730,4 +1546,88 @@ #endif } #endif /* HAVE_POLL */ + +#ifdef HAVE_EPOLL + Py_TYPE(&PyEpoll_Type) = &PyType_Type; + if (PyType_Ready(&PyEpoll_Type) < 0) + return; + + Py_INCREF(&PyEpoll_Type); + PyModule_AddObject(m, "epoll", (PyObject *) &PyEpoll_Type); + + PyModule_AddIntConstant(m, "EPOLL_IN", EPOLLIN); + PyModule_AddIntConstant(m, "EPOLL_OUT", EPOLLOUT); + PyModule_AddIntConstant(m, "EPOLL_PRI", EPOLLPRI); + PyModule_AddIntConstant(m, "EPOLL_ERR", EPOLLERR); + PyModule_AddIntConstant(m, "EPOLL_HUP", EPOLLHUP); + PyModule_AddIntConstant(m, "EPOLL_ET", EPOLLET); +#ifdef EPOLLONESHOT + /* Kernel 2.6.2+ */ + PyModule_AddIntConstant(m, "EPOLL_ONESHOT", EPOLLONESHOT); +#endif + /* PyModule_AddIntConstant(m, "EPOLL_RDHUP", EPOLLRDHUP); */ + PyModule_AddIntConstant(m, "EPOLL_RDNORM", EPOLLRDNORM); + PyModule_AddIntConstant(m, "EPOLL_RDBAND", EPOLLRDBAND); + PyModule_AddIntConstant(m, "EPOLL_WRNORM", EPOLLWRNORM); + PyModule_AddIntConstant(m, "EPOLL_WRBAND", EPOLLWRBAND); + PyModule_AddIntConstant(m, "EPOLL_MSG", EPOLLMSG); + + PyModule_AddIntConstant(m, "EPOLL_CTL_ADD", EPOLL_CTL_ADD); + PyModule_AddIntConstant(m, "EPOLL_CTL_MOD", EPOLL_CTL_MOD); + PyModule_AddIntConstant(m, "EPOLL_CTL_DEL", EPOLL_CTL_DEL); +#endif /* HAVE_EPOLL */ + +#ifdef HAVE_KQUEUE + kqueue_event_Type.tp_new = PyType_GenericNew; + Py_TYPE(&kqueue_event_Type) = &PyType_Type; + if(PyType_Ready(&kqueue_event_Type) < 0) + return; + + Py_INCREF(&kqueue_event_Type); + PyModule_AddObject(m, "kevent", (PyObject *)&kqueue_event_Type); + + Py_TYPE(&kqueue_queue_Type) = &PyType_Type; + if(PyType_Ready(&kqueue_queue_Type) < 0) + return; + Py_INCREF(&kqueue_queue_Type); + PyModule_AddObject(m, "kqueue", (PyObject *)&kqueue_queue_Type); + + // Event filters + PyModule_AddIntConstant(m, "KQE_FILTER_READ", EVFILT_READ); + PyModule_AddIntConstant(m, "KQE_FILTER_WRITE", EVFILT_WRITE); + PyModule_AddIntConstant(m, "KQE_FILTER_AIO", EVFILT_AIO); + PyModule_AddIntConstant(m, "KQE_FILTER_VNODE", EVFILT_VNODE); + PyModule_AddIntConstant(m, "KQE_FILTER_PROC", EVFILT_PROC); + PyModule_AddIntConstant(m, "KQE_FILTER_SIGNAL", EVFILT_SIGNAL); + + // Event flags + PyModule_AddIntConstant(m, "KQE_ADD", EV_ADD); + PyModule_AddIntConstant(m, "KQE_DELETE", EV_DELETE); + PyModule_AddIntConstant(m, "KQE_ENABLE", EV_ENABLE); + PyModule_AddIntConstant(m, "KQE_DISABLE", EV_DISABLE); + PyModule_AddIntConstant(m, "KQE_ONESHOT", EV_ONESHOT); + PyModule_AddIntConstant(m, "KQE_CLEAR", EV_CLEAR); + PyModule_AddIntConstant(m, "KQE_SYSFLAGS", EV_SYSFLAGS); + PyModule_AddIntConstant(m, "KQE_FLAG1", EV_FLAG1); + PyModule_AddIntConstant(m, "KQE_EOF", EV_EOF); + PyModule_AddIntConstant(m, "KQE_ERROR", EV_ERROR); + + // Kernel note flags (for VNODE & PROC filter types) + PyModule_AddIntConstant(m, "KQE_NOTE_DELETE", NOTE_DELETE); + PyModule_AddIntConstant(m, "KQE_NOTE_WRITE", NOTE_WRITE); + PyModule_AddIntConstant(m, "KQE_NOTE_EXTEND", NOTE_EXTEND); + PyModule_AddIntConstant(m, "KQE_NOTE_ATTRIB", NOTE_ATTRIB); + PyModule_AddIntConstant(m, "KQE_NOTE_LINK", NOTE_LINK); + PyModule_AddIntConstant(m, "KQE_NOTE_RENAME", NOTE_RENAME); + + PyModule_AddIntConstant(m, "KQE_NOTE_EXIT", NOTE_EXIT); + PyModule_AddIntConstant(m, "KQE_NOTE_FORK", NOTE_FORK); + PyModule_AddIntConstant(m, "KQE_NOTE_EXEC", NOTE_EXEC); + PyModule_AddIntConstant(m, "KQE_NOTE_PCTRLMASK", NOTE_PCTRLMASK); + PyModule_AddIntConstant(m, "KQE_NOTE_PDATAMASK", NOTE_PDATAMASK); + + PyModule_AddIntConstant(m, "KQE_NOTE_TRACK", NOTE_TRACK); + PyModule_AddIntConstant(m, "KQE_NOTE_TRACKERR", NOTE_TRACKERR); + PyModule_AddIntConstant(m, "KQE_NOTE_CHILD", NOTE_CHILD); +#endif /* HAVE_KQUEUE */ } Index: pyconfig.h.in =================================================================== --- pyconfig.h.in (Revision 59566) +++ pyconfig.h.in (Arbeitskopie) @@ -138,6 +138,9 @@ /* Defined when any dynamic module loading is enabled. */ #undef HAVE_DYNAMIC_LOADING +/* Define if you have the 'epoll' functions. */ +#undef HAVE_EPOLL + /* Define to 1 if you have the header file. */ #undef HAVE_ERRNO_H @@ -306,6 +309,9 @@ /* Define to 1 if you have the `killpg' function. */ #undef HAVE_KILLPG +/* Define if you have the 'kqueue' functions. */ +#undef HAVE_KQUEUE + /* Define to 1 if you have the header file. */ #undef HAVE_LANGINFO_H @@ -612,6 +618,12 @@ */ #undef HAVE_SYS_DIR_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EPOLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EVENT_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_FILE_H @@ -1040,4 +1052,3 @@ #endif /*Py_PYCONFIG_H*/ -