import time
import traceback
import math
+import weakref
from tornado.concurrent import Future, is_future, chain_future, future_set_exc_info, future_add_done_callback # noqa: E501
from tornado.log import app_log, gen_log
WRITE = _EPOLLOUT
ERROR = _EPOLLERR | _EPOLLHUP
- # Global lock for creating global IOLoop instance
- _instance_lock = threading.Lock()
-
+ # In Python 2, _current.instance points to the current IOLoop.
_current = threading.local()
+ # In Python 3, _ioloop_for_asyncio maps from asyncio loops to IOLoops.
+ _ioloop_for_asyncio = weakref.WeakKeyDictionary()
+
@classmethod
def configure(cls, impl, **kwargs):
if asyncio is not None:
This method only knows about `IOLoop` objects (and not, for
example, `asyncio` event loops), so it is of limited use.
"""
- return IOLoop.current(instance=False) is not None
+ if asyncio is None:
+ return IOLoop.current(instance=False) is not None
+ else:
+ # Asyncio gives us no way to ask whether an instance
+ # exists without creating one. Even calling
+ # `IOLoop.current(instance=False)` will create the asyncio
+ # loop (but not the Tornado loop, which would break the
+ # ability to use this function for "is it safe to fork?".
+ return False
def install(self):
"""Deprecated alias for `make_current()`.
Added ``instance`` argument to control the fallback to
`IOLoop.instance()`.
.. versionchanged:: 5.0
+ On Python 3, control of the current `IOLoop` is delegated
+ to `asyncio`, with this and other methods as pass-through accessors.
The ``instance`` argument now controls whether an `IOLoop`
is created automatically when there is none, instead of
whether we fall back to `IOLoop.instance()` (which is now
- an alias for this method)
+ an alias for this method). ``instance=False`` is deprecated,
+ since even if we do not create an `IOLoop`, this method
+ may initialize the asyncio loop.
"""
- current = getattr(IOLoop._current, "instance", None)
- if current is None and instance:
- current = None
- if asyncio is not None:
- from tornado.platform.asyncio import AsyncIOLoop, AsyncIOMainLoop
- if IOLoop.configured_class() is AsyncIOLoop:
- current = AsyncIOMainLoop()
- if current is None:
+ if asyncio is None:
+ current = getattr(IOLoop._current, "instance", None)
+ if current is None and instance:
current = IOLoop()
- if IOLoop._current.instance is not current:
- raise RuntimeError("new IOLoop did not become current")
+ if IOLoop._current.instance is not current:
+ raise RuntimeError("new IOLoop did not become current")
+ else:
+ try:
+ loop = asyncio.get_event_loop()
+ except RuntimeError:
+ if not instance:
+ return None
+ raise
+ try:
+ return IOLoop._ioloop_for_asyncio[loop]
+ except KeyError:
+ if instance:
+ from tornado.platform.asyncio import AsyncIOMainLoop
+ current = AsyncIOMainLoop(make_current=True)
+ else:
+ current = None
return current
def make_current(self):
.. versionchanged:: 5.0
This method also sets the current `asyncio` event loop.
"""
+ # The asyncio event loops override this method.
+ assert asyncio is None
old = getattr(IOLoop._current, "instance", None)
if old is not None:
old.clear_current()
.. versionchanged:: 5.0
This method also clears the current `asyncio` event loop.
"""
- old = getattr(IOLoop._current, "instance", None)
+ old = IOLoop.current(instance=False)
if old is not None:
old._clear_current_hook()
- IOLoop._current.instance = None
+ if asyncio is None:
+ IOLoop._current.instance = None
def _clear_current_hook(self):
"""Instance method called when an IOLoop ceases to be current.
if IOLoop.current(instance=False) is None:
self.make_current()
elif make_current:
- if IOLoop.current(instance=False) is not None:
+ current = IOLoop.current(instance=False)
+ # AsyncIO loops can already be current by this point.
+ if current is not None and current is not self:
raise RuntimeError("current IOLoop already exists")
self.make_current()
self.readers = set()
self.writers = set()
self.closing = False
+ IOLoop._ioloop_for_asyncio[asyncio_loop] = self
super(BaseAsyncIOLoop, self).initialize(**kwargs)
def close(self, all_fds=False):
handler_func(fileobj, events)
def start(self):
- old_current = IOLoop.current(instance=False)
+ try:
+ old_loop = asyncio.get_event_loop()
+ except RuntimeError:
+ old_loop = None
try:
self._setup_logging()
- self.make_current()
+ asyncio.set_event_loop(self.asyncio_loop)
self.asyncio_loop.run_forever()
finally:
- if old_current is None:
- IOLoop.clear_current()
- else:
- old_current.make_current()
+ asyncio.set_event_loop(old_loop)
def stop(self):
self.asyncio_loop.stop()
def initialize(self, **kwargs):
super(AsyncIOMainLoop, self).initialize(asyncio.get_event_loop(), **kwargs)
+ def make_current(self):
+ # AsyncIOMainLoop already refers to the current asyncio loop so
+ # nothing to do here.
+ pass
+
class AsyncIOLoop(BaseAsyncIOLoop):
"""``AsyncIOLoop`` is an `.IOLoop` that runs on an ``asyncio`` event loop.
def make_current(self):
if not self.is_current:
- super(AsyncIOLoop, self).make_current()
try:
self.old_asyncio = asyncio.get_event_loop()
except RuntimeError: