]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add a kqueue-based implementation of IOLoop for BSD/Mac systems.
authorBen Darnell <bdarnell@beaker.local>
Fri, 8 Jan 2010 02:53:47 +0000 (18:53 -0800)
committerBen Darnell <bdarnell@beaker.local>
Fri, 8 Jan 2010 02:53:47 +0000 (18:53 -0800)
The main reason for this is to be able to detect when a connection has
been closed on the other side.
(http://github.com/facebook/tornado/issues/#issue/37)

This has been supported with epoll but not select, and it turned out to be
easier to add kqueue support than to figure out how to detect and handle
this case with select.

This change has not been tested in a production environment, but I have tested
it on my mac (10.6) and everything appears to work, and
IOStream.set_close_callback now works as expected.

tornado/ioloop.py

index ec7450a9e36c0c1ef29450a4bd3520a9a4600597..5c7f194f2d55977957fbca934195c45268728b91 100644 (file)
@@ -335,6 +335,47 @@ class _EPoll(object):
         return epoll.epoll_wait(self._epoll_fd, int(timeout * 1000))
 
 
+class _KQueue(object):
+    """A kqueue-based event loop for BSD/Mac systems."""
+    def __init__(self):
+        self._kqueue = select.kqueue()
+        self._filters = {}
+
+    def register(self, fd, events):
+        filter = 0
+        if events & IOLoop.WRITE:
+            filter |= select.KQ_FILTER_WRITE
+        if events & IOLoop.READ or filter == 0:
+            filter |= select.KQ_FILTER_READ
+        self._filters[fd] = filter
+        kevent = select.kevent(fd, filter=filter)
+        self._kqueue.control([kevent], 0)
+
+    def modify(self, fd, events):
+        self.unregister(fd)
+        self.register(fd, events)
+
+    def unregister(self, fd):
+        kevent = select.kevent(fd, filter=self._filters[fd],
+                               flags=select.KQ_EV_DELETE)
+        self._kqueue.control([kevent], 0)
+
+    def poll(self, timeout):
+        kevents = self._kqueue.control(None, 1000, timeout)
+        events = []
+        for kevent in kevents:
+            fd = kevent.ident
+            flags = 0
+            if kevent.filter & select.KQ_FILTER_READ:
+                flags |= IOLoop.READ
+            if kevent.filter & select.KQ_FILTER_WRITE:
+                flags |= IOLoop.WRITE
+            if kevent.flags & select.KQ_EV_ERROR:
+                flags |= IOLoop.ERROR
+            events.append((fd, flags))
+        return events
+
+
 class _Select(object):
     """A simple, select()-based IOLoop implementation for non-Linux systems"""
     def __init__(self):
@@ -375,6 +416,9 @@ class _Select(object):
 if hasattr(select, "epoll"):
     # Python 2.6+ on Linux
     _poll = select.epoll
+elif hasattr(select, "kqueue"):
+    # Python 2.6+ on BSD or Mac
+    _poll = _KQueue
 else:
     try:
         # Linux systems with our C module installed