From: Min RK Date: Mon, 10 May 2021 08:21:37 +0000 (+0200) Subject: separate SelectorThread into its own object X-Git-Tag: v6.4.0b1~32^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=289c834bedac07761f0cd33aee9854c4efb0c5e5;p=thirdparty%2Ftornado.git separate SelectorThread into its own object so it can be used on an existing asyncio loop without replacing the loop itself --- diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py index 292d9b66a..b07af3a95 100644 --- a/tornado/platform/asyncio.py +++ b/tornado/platform/asyncio.py @@ -51,7 +51,7 @@ _T = TypeVar("_T") # Collection of selector thread event loops to shut down on exit. -_selector_loops = set() # type: Set[AddThreadSelectorEventLoop] +_selector_loops = set() # type: Set[SelectorThread] def _atexit_callback() -> None: @@ -401,54 +401,17 @@ class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore return loop -class AddThreadSelectorEventLoop(asyncio.AbstractEventLoop): - """Wrap an event loop to add implementations of the ``add_reader`` method family. +class SelectorThread: + """Define ``add_reader`` methods to be called in a background select thread. Instances of this class start a second thread to run a selector. - This thread is completely hidden from the user; all callbacks are - run on the wrapped event loop's thread. - - This class is used automatically by Tornado; applications should not need - to refer to it directly. - - It is safe to wrap any event loop with this class, although it only makes sense - for event loops that do not implement the ``add_reader`` family of methods - themselves (i.e. ``WindowsProactorEventLoop``) - - Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop. + This thread is completely hidden from the user; + all callbacks are run on the wrapped event loop's thread. + Typically used via ``AddThreadSelectorEventLoop``, + but can be attached to a running asyncio loop. """ - # This class is a __getattribute__-based proxy. All attributes other than those - # in this set are proxied through to the underlying loop. - MY_ATTRIBUTES = { - "_consume_waker", - "_select_cond", - "_select_args", - "_closing_selector", - "_thread", - "_handle_event", - "_readers", - "_real_loop", - "_start_select", - "_run_select", - "_handle_select", - "_wake_selector", - "_waker_r", - "_waker_w", - "_writers", - "add_reader", - "add_writer", - "close", - "remove_reader", - "remove_writer", - } - - def __getattribute__(self, name: str) -> Any: - if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES: - return super().__getattribute__(name) - return getattr(self._real_loop, name) - def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None: self._real_loop = real_loop @@ -501,7 +464,6 @@ class AddThreadSelectorEventLoop(asyncio.AbstractEventLoop): _selector_loops.discard(self) self._waker_r.close() self._waker_w.close() - self._real_loop.close() def _wake_selector(self) -> None: try: @@ -613,3 +575,63 @@ class AddThreadSelectorEventLoop(asyncio.AbstractEventLoop): def remove_writer(self, fd: "_FileDescriptorLike") -> None: del self._writers[fd] self._wake_selector() + + +class AddThreadSelectorEventLoop(asyncio.AbstractEventLoop): + """Wrap an event loop to add implementations of the ``add_reader`` method family. + + Instances of this class start a second thread to run a selector. + This thread is completely hidden from the user; all callbacks are + run on the wrapped event loop's thread. + + This class is used automatically by Tornado; applications should not need + to refer to it directly. + + It is safe to wrap any event loop with this class, although it only makes sense + for event loops that do not implement the ``add_reader`` family of methods + themselves (i.e. ``WindowsProactorEventLoop``) + + Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop. + + """ + + # This class is a __getattribute__-based proxy. All attributes other than those + # in this set are proxied through to the underlying loop. + MY_ATTRIBUTES = { + "_real_loop", + "_selector", + "add_reader", + "add_writer", + "close", + "remove_reader", + "remove_writer", + } + + def __getattribute__(self, name: str) -> Any: + if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES: + return super().__getattribute__(name) + return getattr(self._real_loop, name) + + def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None: + self._real_loop = real_loop + self._selector = SelectorThread(real_loop) + + def close(self) -> None: + self._selector.close() + self._real_loop.close() + + def add_reader( + self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any + ) -> None: + return self._selector.add_reader(fd, callback, *args) + + def add_writer( + self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any + ) -> None: + return self._selector.add_writer(fd, callback, *args) + + def remove_reader(self, fd: "_FileDescriptorLike") -> None: + return self._selector.remove_reader(fd) + + def remove_writer(self, fd: "_FileDescriptorLike") -> None: + return self._selector.remove_writer(fd)