if loop.is_closed():
del IOLoop._ioloop_for_asyncio[loop]
IOLoop._ioloop_for_asyncio[asyncio_loop] = self
+
+ self._thread_identity = 0
+
super(BaseAsyncIOLoop, self).initialize(**kwargs)
- def close(self, all_fds: bool=False) -> None:
+ def assign_thread_identity() -> None:
+ self._thread_identity = get_ident()
+
+ self.add_callback(assign_thread_identity)
+
+ def close(self, all_fds: bool = False) -> None:
self.closing = True
for fd in list(self.handlers):
fileobj, handler_func = self.handlers[fd]
timeout.cancel() # type: ignore
def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
+ if get_ident() == self._thread_identity:
+ call_soon = self.asyncio_loop.call_soon
+ else:
+ call_soon = self.asyncio_loop.call_soon_threadsafe
try:
- self.asyncio_loop.call_soon_threadsafe(
- self._run_callback, functools.partial(callback, *args, **kwargs)
- )
- call_soon(
- self._run_callback,
- functools.partial(callback, *args, **kwargs))
++ call_soon(self._run_callback, functools.partial(callback, *args, **kwargs))
except RuntimeError:
# "Event loop is closed". Swallow the exception for
# consistency with PollIOLoop (and logical consistency
# eventually execute).
pass
- add_callback_from_signal = add_callback
- def add_callback_from_signal(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
++ def add_callback_from_signal(
++ self, callback: Callable, *args: Any, **kwargs: Any
++ ) -> None:
+ try:
+ self.asyncio_loop.call_soon_threadsafe(
- self._run_callback,
- functools.partial(callback, *args, **kwargs))
++ self._run_callback, functools.partial(callback, *args, **kwargs)
++ )
+ except RuntimeError:
+ pass
- def run_in_executor(self, executor: Optional[concurrent.futures.Executor],
- func: Callable[..., _T], *args: Any) -> Awaitable[_T]:
+ def run_in_executor(
+ self,
+ executor: Optional[concurrent.futures.Executor],
+ func: Callable[..., _T],
+ *args: Any
+ ) -> Awaitable[_T]:
return self.asyncio_loop.run_in_executor(executor, func, *args)
def set_default_executor(self, executor: concurrent.futures.Executor) -> None:
@gen.coroutine
def init_server():
sock, self.port = bind_unused_port()
- app = Application([('/', HelloWorldHandler)])
+ app = Application([("/", HelloWorldHandler)])
self.server = HTTPServer(app)
self.server.add_socket(sock)
+ event.set()
- self.server_ioloop.run_sync(init_server)
+ def start():
+ self.server_ioloop.run_sync(init_server)
+ self.server_ioloop.start()
- self.server_thread = threading.Thread(target=self.server_ioloop.start)
+ self.server_thread = threading.Thread(target=start)
self.server_thread.start()
+ event.wait()
self.http_client = HTTPClient()
.. versionchanged:: 3.1
Many of the methods for subclasses were added in Tornado 3.1.
"""
+
CACHE_MAX_AGE = 86400 * 365 * 10 # 10 years
- _static_hashes = {} # type: typing.Dict
+ _static_hashes = {} # type: Dict[str, Optional[str]]
_lock = threading.Lock() # protects _static_hashes
- def initialize(
- self, path: str, default_filename: str = None
- ) -> None:
- def initialize(self, path, default_filename=None):
++ def initialize(self, path: str, default_filename: str = None) -> None:
self.root = path
self.default_filename = default_filename