]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Fix ioloop._KQueue to work correctly when listening for both read and write.
authorBen Darnell <bdarnell@beaker.local>
Tue, 16 Feb 2010 20:47:32 +0000 (12:47 -0800)
committerBen Darnell <bdarnell@beaker.local>
Tue, 16 Feb 2010 20:53:23 +0000 (12:53 -0800)
kqueue.control must be called separately for each type of filter, not just
once with the filters or'd together.

Fixes http://github.com/facebook/tornado/issues/issue/59/
Change adapted from
http://github.com/weaver/tornado/commit/97e528cf6b92bbb590579864962e2d51c22202e0

tornado/ioloop.py

index 35d90cd1c8affc34084fe94d9819f950ddde41a4..44cdffdeaaa70ff9896517b5e4d19e17220c14b9 100644 (file)
@@ -339,41 +339,47 @@ class _KQueue(object):
     """A kqueue-based event loop for BSD/Mac systems."""
     def __init__(self):
         self._kqueue = select.kqueue()
-        self._filters = {}
+        self._active = {}
 
     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)
+        self._control(fd, events, select.KQ_EV_ADD)
+        self._active[fd] = events
 
     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)
+        events = self._active.pop(fd)
+        self._control(fd, events, select.KQ_EV_DELETE)
+
+    def _control(self, fd, events, flags):
+        kevents = []
+        if events & IOLoop.WRITE:
+            kevents.append(select.kevent(
+                    fd, filter=select.KQ_FILTER_WRITE, flags=flags))
+        if events & IOLoop.READ or not kevents:
+            # Always read when there is not a write
+            kevents.append(select.kevent(
+                    fd, filter=select.KQ_FILTER_READ, flags=flags))
+        # Even though control() takes a list, it seems to return EINVAL
+        # on Mac OS X (10.6) when there is more than one event in the list.
+        for kevent in kevents:
+            self._kqueue.control([kevent], 0)
 
     def poll(self, timeout):
         kevents = self._kqueue.control(None, 1000, timeout)
-        events = []
+        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.filter == select.KQ_FILTER_READ:
+                events[fd] = events.get(fd, 0) | IOLoop.READ
+            if kevent.filter == select.KQ_FILTER_WRITE:
+                events[fd] = events.get(fd, 0) | IOLoop.WRITE
             if kevent.flags & select.KQ_EV_ERROR:
-                flags |= IOLoop.ERROR
-            events.append((fd, flags))
-        return events
+                events[fd] = events.get(fd, 0) | IOLoop.ERROR
+        return events.items()
 
 
 class _Select(object):