Index: configure =================================================================== --- configure (Revision 59563) +++ 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,14 @@ + 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/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 +15645,57 @@ 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 # 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 59563) +++ 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/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,12 @@ 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) +) # 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: Lib/test/test_epoll.py =================================================================== --- Lib/test/test_epoll.py (Revision 0) +++ Lib/test/test_epoll.py (Revision 0) @@ -0,0 +1,167 @@ +""" +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.control(select.EPOLL_CTL_ADD, server.fileno(), + select.EPOLL_IN | select.EPOLL_OUT) + ep.control(select.EPOLL_CTL_ADD, 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.control(select.EPOLL_CTL_ADD, server.fileno(), + select.EPOLL_IN | select.EPOLL_OUT | select.EPOLL_ET) + ep.control(select.EPOLL_CTL_ADD, 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) + + def test_errors(self): + """ + Test some error conditions of epoll methods. + """ + self.assertRaises(IOError, select.epoll, -1) + #self.assertRaises(IOError, epoll.control, -1, epoll.CTL_ADD, -1, epoll.IN) + #self.assertRaises(IOError, epoll.wait, -1, 4, 1000) + #self.assertRaises(IOError, epoll.close, -1) + +if not hasattr(select, "epoll") is None: + EPoll.skip = "epoll module 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 59564) +++ Modules/selectmodule.c (Arbeitskopie) @@ -31,6 +31,10 @@ #include #endif +#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL) +#include +#endif + #ifdef __sgi /* This is missing from unistd.h */ extern void bzero(void *, int); @@ -483,9 +487,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 +650,267 @@ #endif /* HAVE_POLL */ +#ifdef HAVE_EPOLL +/* epoll support + */ + +typedef struct { + PyObject_HEAD + int epfd; +} PyEpoll_Object; + +static PyTypeObject PyEpoll_Type; +#define epoll_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; + + self->epfd = epoll_create(size); + if (self->epfd < 0) { + 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_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 PyLong_FromLong((long)self->epfd); +} + +PyDoc_STRVAR(pyepoll_fileno_doc, +"fileno() -> int\n\ +\n\ +Return the epoll control file descriptor."); + +static PyObject * +pyepoll_control(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int op, fd; + unsigned int events; + int result; + struct epoll_event ev; + static char *kwlist[] = {"op", "fd", "events", NULL}; + + if (self->epfd < 0) + return pyepoll_err_closed(); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiI:control", kwlist, + &op, &fd, &events)) + return NULL; + + ev.events = events; + ev.data.fd = fd; + + Py_BEGIN_ALLOW_THREADS + result = epoll_ctl(self->epfd, op, fd, &ev); + Py_END_ALLOW_THREADS + if (result < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pyepoll_control_doc, +"control(op, fd, events) -> None\n\ +\n\ +op is one of EPOLL_CTL_ADD, EPOLL_CTL_DEL or EPOLL_CTL_MOD\n\ +fd is the target file descriptor of the operation\n\ +events is a bit set composed of the various EPOLL constants\n\ +\n\ +The epoll interface supports all file descriptors that support poll."); + +static PyObject * +pyepoll_wait(PyEpoll_Object *self, PyObject *args, PyObject *kwds) +{ + int timeout, nfds, i; + int maxevents; + struct epoll_event *evs; + PyObject* elist; + 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; + + /* calloc to set the content to 0 */ + evs = (struct epoll_event*) calloc(maxevents, + sizeof(struct epoll_event)); + if (evs == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + nfds = epoll_wait(self->epfd, evs, maxevents, timeout); + Py_END_ALLOW_THREADS + if (nfds < 0) { + free(evs); + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + + elist = PyList_New(nfds); + if (elist == NULL) { + free(evs); + return NULL; + } + + for (i = 0; i < nfds; i++) { + PyObject *eptuple; + eptuple = Py_BuildValue("iI", evs[i].data.fd, + evs[i].events); + PyList_SET_ITEM(elist, i, eptuple); + } + 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}, + {"control", (PyCFunction)pyepoll_control, + METH_VARARGS | METH_KEYWORDS, pyepoll_control_doc}, + {"fileno", (PyCFunction)pyepoll_fileno, METH_NOARGS, + pyepoll_fileno_doc}, + {"wait", (PyCFunction)pyepoll_wait, + METH_VARARGS | METH_KEYWORDS, pyepoll_wait_doc}, + {NULL, NULL}, +}; + +static PyGetSetDef pyepoll_getsetlist[] = { + {"closed", (getter)pyepoll_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 */ + PyDoc_STRVAR(select_doc, "select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)\n\ \n\ @@ -730,4 +995,33 @@ #endif } #endif /* HAVE_POLL */ +#ifdef HAVE_EPOLL + Py_TYPE(&PyEpoll_Type) = &PyType_Type; + if (PyType_Ready(&PyEpoll_Type) < 0) + return; + 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 + PyModule_AddIntConstant(m, "EPOLL_ONESHOT", EPOLLONESHOT); +#endif + PyModule_AddIntConstant(m, "EPOLL_RDNORM", EPOLLRDNORM); + PyModule_AddIntConstant(m, "EPOLL_RDBAND", EPOLLRDBAND); +#ifdef EPOLLRDHUP + PyModule_AddIntConstant(m, "EPOLL_RDHUP", EPOLLRDHUP); +#endif + 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 */ } Index: pyconfig.h.in =================================================================== --- pyconfig.h.in (Revision 59563) +++ 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 @@ -612,6 +615,9 @@ */ #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_FILE_H @@ -1040,4 +1046,3 @@ #endif /*Py_PYCONFIG_H*/ -