New submission from Christian Heimes:

The patch implements Linux's epoll interface
(http://linux.die.net/man/4/epoll). My patch doesn't introduce a new
module like http://bugs.python.org/issue1675118 and it wraps the epoll
control fd in an object like Twisted's _epoll.pyd interface.

My interface is almost identical to Twisted's API except for some names.
I named my control function "control" instead of "_control" and the
constants are all named "select.EPOLL_SPAM" instead of "SPAM".

Missing:
Documentation

----------
assignee: tiran
files: trunk_select_epoll.patch
keywords: patch, py3k
messages: 58795
nosy: tiran
priority: normal
severity: normal
status: open
title: [patch] select.epoll wrapper for Linux 2.6 epoll()
type: rfe
versions: Python 2.6, Python 3.0
Added file: http://bugs.python.org/file8992/trunk_select_epoll.patch

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1657>
__________________________________
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 <sys/epoll.h>
+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 <sys/epoll.h>], 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 <sys/poll.h>
 #endif
 
+#if defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL)
+#include <sys/epoll.h>
+#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 <errno.h> header file. */
 #undef HAVE_ERRNO_H
 
@@ -612,6 +615,9 @@
    */
 #undef HAVE_SYS_DIR_H
 
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
 /* Define to 1 if you have the <sys/file.h> header file. */
 #undef HAVE_SYS_FILE_H
 
@@ -1040,4 +1046,3 @@
 
 #endif /*Py_PYCONFIG_H*/
 
-
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to