Permalink
Browse files

Move IOLoop subclasses to their own modules under platform.

  • Loading branch information...
1 parent 832ac4f commit 8b32830d73e34b09137310c69c1ece2714975af4 @bdarnell bdarnell committed Oct 6, 2012
Showing with 257 additions and 175 deletions.
  1. +10 −2 maint/vm/ubuntu12.04/tox.ini
  2. +12 −172 tornado/ioloop.py
  3. +68 −0 tornado/platform/epoll.py
  4. +91 −0 tornado/platform/kqueue.py
  5. +75 −0 tornado/platform/select.py
  6. +1 −1 tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27-full, py25-full, py32, py25, py27
+envlist = py27-full, py25-full, py32, py25, py27, py27-select
setupdir=/tornado
toxworkdir=/home/vagrant/tox-tornado
@@ -27,4 +27,12 @@ basepython = python2.7
deps =
futures
pycurl
- twisted==12.2.0
+ twisted==12.2.0
+
+[testenv:py27-select]
+basepython = python2.7
+deps =
+ futures
+ pycurl
+ twisted==12.2.0
+commands = python -m tornado.test.runtests --ioloop=tornado.platform.select.SelectIOLoop {posargs:}
View
@@ -35,6 +35,7 @@
import logging
import os
import select
+import sys
import thread
import threading
import time
@@ -103,20 +104,19 @@ def configurable_base(cls):
@classmethod
def configurable_default(cls):
- if hasattr(select, "epoll"):
- # Python 2.6+ on Linux
- return EPollIOLoop
- elif hasattr(select, "kqueue"):
+ if hasattr(select, "epoll") or sys.platform.startswith('linux'):
+ try:
+ from tornado.platform.epoll import EPollIOLoop
+ return EPollIOLoop
+ except ImportError:
+ gen_log.warning("unable to import EPollIOLoop, falling back to SelectIOLoop")
+ pass
+ if hasattr(select, "kqueue"):
# Python 2.6+ on BSD or Mac
+ from tornado.platform.kqueue import KQueueIOLoop
return KQueueIOLoop
- else:
- try:
- # Python 2.5 on Linux with our C module installed
- from tornado import epoll
- return EPoll25IOLoop
- except Exception:
- # Everything else
- return SelectIOLoop
+ from tornado.platform.select import SelectIOLoop
+ return SelectIOLoop
# Constants from the epoll module
_EPOLLIN = 0x001
@@ -663,163 +663,3 @@ def _schedule_next(self):
while self._next_timeout <= current_time:
self._next_timeout += self.callback_time / 1000.0
self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run)
-
-
-class _EPoll(object):
- """An epoll-based event loop using our C module for Python 2.5 systems"""
- _EPOLL_CTL_ADD = 1
- _EPOLL_CTL_DEL = 2
- _EPOLL_CTL_MOD = 3
-
- def __init__(self):
- from tornado import epoll
- self.epoll = epoll
- self._epoll_fd = epoll.epoll_create()
-
- def fileno(self):
- return self._epoll_fd
-
- def close(self):
- os.close(self._epoll_fd)
-
- def register(self, fd, events):
- self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_ADD, fd, events)
-
- def modify(self, fd, events):
- self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_MOD, fd, events)
-
- def unregister(self, fd):
- self.epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_DEL, fd, 0)
-
- def poll(self, timeout):
- return self.epoll.epoll_wait(self._epoll_fd, int(timeout * 1000))
-
-
-class EPoll25IOLoop(IOLoop):
- def initialize(self, **kwargs):
- super(EPoll25IOLoop, self).initialize(impl=_EPoll(), **kwargs)
-
-
-class _KQueue(object):
- """A kqueue-based event loop for BSD/Mac systems."""
- def __init__(self):
- self._kqueue = select.kqueue()
- self._active = {}
-
- def fileno(self):
- return self._kqueue.fileno()
-
- def close(self):
- self._kqueue.close()
-
- def register(self, fd, events):
- if fd in self._active:
- raise IOError("fd %d already registered" % fd)
- 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):
- 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 = {}
- for kevent in kevents:
- fd = kevent.ident
- if kevent.filter == select.KQ_FILTER_READ:
- events[fd] = events.get(fd, 0) | IOLoop.READ
- if kevent.filter == select.KQ_FILTER_WRITE:
- if kevent.flags & select.KQ_EV_EOF:
- # If an asynchronous connection is refused, kqueue
- # returns a write event with the EOF flag set.
- # Turn this into an error for consistency with the
- # other IOLoop implementations.
- # Note that for read events, EOF may be returned before
- # all data has been consumed from the socket buffer,
- # so we only check for EOF on write events.
- events[fd] = IOLoop.ERROR
- else:
- events[fd] = events.get(fd, 0) | IOLoop.WRITE
- if kevent.flags & select.KQ_EV_ERROR:
- events[fd] = events.get(fd, 0) | IOLoop.ERROR
- return events.items()
-
-
-class KQueueIOLoop(IOLoop):
- def initialize(self, **kwargs):
- super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)
-
-
-class _Select(object):
- """A simple, select()-based IOLoop implementation for non-Linux systems"""
- def __init__(self):
- self.read_fds = set()
- self.write_fds = set()
- self.error_fds = set()
- self.fd_sets = (self.read_fds, self.write_fds, self.error_fds)
-
- def close(self):
- pass
-
- def register(self, fd, events):
- if fd in self.read_fds or fd in self.write_fds or fd in self.error_fds:
- raise IOError("fd %d already registered" % fd)
- if events & IOLoop.READ:
- self.read_fds.add(fd)
- if events & IOLoop.WRITE:
- self.write_fds.add(fd)
- if events & IOLoop.ERROR:
- self.error_fds.add(fd)
- # Closed connections are reported as errors by epoll and kqueue,
- # but as zero-byte reads by select, so when errors are requested
- # we need to listen for both read and error.
- self.read_fds.add(fd)
-
- def modify(self, fd, events):
- self.unregister(fd)
- self.register(fd, events)
-
- def unregister(self, fd):
- self.read_fds.discard(fd)
- self.write_fds.discard(fd)
- self.error_fds.discard(fd)
-
- def poll(self, timeout):
- readable, writeable, errors = select.select(
- self.read_fds, self.write_fds, self.error_fds, timeout)
- events = {}
- for fd in readable:
- events[fd] = events.get(fd, 0) | IOLoop.READ
- for fd in writeable:
- events[fd] = events.get(fd, 0) | IOLoop.WRITE
- for fd in errors:
- events[fd] = events.get(fd, 0) | IOLoop.ERROR
- return events.items()
-
-class SelectIOLoop(IOLoop):
- def initialize(self, **kwargs):
- super(SelectIOLoop, self).initialize(impl=_Select(), **kwargs)
-
-
-class EPollIOLoop(IOLoop):
- def initialize(self, **kwargs):
- super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
View
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 Facebook
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+"""EPoll-based IOLoop implementation for Linux systems.
+
+Supports the standard library's `select.epoll` function for Python 2.6+,
+and our own C module for Python 2.5.
+"""
+from __future__ import absolute_import, division, with_statement
+
+import os
+import select
+
+from tornado.ioloop import IOLoop
+
+if hasattr(select, 'epoll'):
+ # Python 2.6+
+ class EPollIOLoop(IOLoop):
+ def initialize(self, **kwargs):
+ super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
+else:
+ # Python 2.5
+ from tornado import epoll
+
+ class _EPoll(object):
+ """An epoll-based event loop using our C module for Python 2.5 systems"""
+ _EPOLL_CTL_ADD = 1
+ _EPOLL_CTL_DEL = 2
+ _EPOLL_CTL_MOD = 3
+
+ def __init__(self):
+ self._epoll_fd = epoll.epoll_create()
+
+ def fileno(self):
+ return self._epoll_fd
+
+ def close(self):
+ os.close(self._epoll_fd)
+
+ def register(self, fd, events):
+ epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_ADD, fd, events)
+
+ def modify(self, fd, events):
+ epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_MOD, fd, events)
+
+ def unregister(self, fd):
+ epoll.epoll_ctl(self._epoll_fd, self._EPOLL_CTL_DEL, fd, 0)
+
+ def poll(self, timeout):
+ return epoll.epoll_wait(self._epoll_fd, int(timeout * 1000))
+
+
+ class EPollIOLoop(IOLoop):
+ def initialize(self, **kwargs):
+ super(EPollIOLoop, self).initialize(impl=_EPoll(), **kwargs)
+
View
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 Facebook
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+"""KQueue-based IOLoop implementation for BSD/Mac systems."""
+from __future__ import absolute_import, division, with_statement
+
+import select
+
+from tornado.ioloop import IOLoop
+
+assert hasattr(select, 'kqueue'), 'kqueue not supported'
+
+class _KQueue(object):
+ """A kqueue-based event loop for BSD/Mac systems."""
+ def __init__(self):
+ self._kqueue = select.kqueue()
+ self._active = {}
+
+ def fileno(self):
+ return self._kqueue.fileno()
+
+ def close(self):
+ self._kqueue.close()
+
+ def register(self, fd, events):
+ if fd in self._active:
+ raise IOError("fd %d already registered" % fd)
+ 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):
+ 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 = {}
+ for kevent in kevents:
+ fd = kevent.ident
+ if kevent.filter == select.KQ_FILTER_READ:
+ events[fd] = events.get(fd, 0) | IOLoop.READ
+ if kevent.filter == select.KQ_FILTER_WRITE:
+ if kevent.flags & select.KQ_EV_EOF:
+ # If an asynchronous connection is refused, kqueue
+ # returns a write event with the EOF flag set.
+ # Turn this into an error for consistency with the
+ # other IOLoop implementations.
+ # Note that for read events, EOF may be returned before
+ # all data has been consumed from the socket buffer,
+ # so we only check for EOF on write events.
+ events[fd] = IOLoop.ERROR
+ else:
+ events[fd] = events.get(fd, 0) | IOLoop.WRITE
+ if kevent.flags & select.KQ_EV_ERROR:
+ events[fd] = events.get(fd, 0) | IOLoop.ERROR
+ return events.items()
+
+
+class KQueueIOLoop(IOLoop):
+ def initialize(self, **kwargs):
+ super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)
Oops, something went wrong.

0 comments on commit 8b32830

Please sign in to comment.