{
    "file_diffs": [
        {
            "old_file_content": "\"\"\"Bridges between the `asyncio` module and Tornado IOLoop.\n\n.. versionadded:: 3.2\n\nThis module integrates Tornado with the ``asyncio`` module introduced\nin Python 3.4. This makes it possible to combine the two libraries on\nthe same event loop.\n\n.. deprecated:: 5.0\n\n   While the code in this module is still used, it is now enabled\n   automatically when `asyncio` is available, so applications should\n   no longer need to refer to this module directly.\n\n.. note::\n\n   Tornado is designed to use a selector-based event loop. On Windows,\n   where a proactor-based event loop has been the default since Python 3.8,\n   a selector event loop is emulated by running ``select`` on a separate thread.\n   Configuring ``asyncio`` to use a selector event loop may improve performance\n   of Tornado (but may reduce performance of other ``asyncio``-based libraries\n   in the same process).\n\"\"\"\n\nimport asyncio\nimport atexit\nimport concurrent.futures\nimport errno\nimport functools\nimport select\nimport socket\nimport sys\nimport threading\nimport typing\nfrom tornado.gen import convert_yielded\nfrom tornado.ioloop import IOLoop, _Selectable\n\nfrom typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict\n\nif typing.TYPE_CHECKING:\n    from typing import Set  # noqa: F401\n    from typing_extensions import Protocol\n\n    class _HasFileno(Protocol):\n        def fileno(self) -> int:\n            pass\n\n    _FileDescriptorLike = Union[int, _HasFileno]\n\n_T = TypeVar(\"_T\")\n\n\n# Collection of selector thread event loops to shut down on exit.\n_selector_loops = set()  # type: Set[SelectorThread]\n\n\ndef _atexit_callback() -> None:\n    for loop in _selector_loops:\n        with loop._select_cond:\n            loop._closing_selector = True\n            loop._select_cond.notify()\n        try:\n            loop._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n        # If we don't join our (daemon) thread here, we may get a deadlock\n        # during interpreter shutdown. I don't really understand why. This\n        # deadlock happens every time in CI (both travis and appveyor) but\n        # I've never been able to reproduce locally.\n        loop._thread.join()\n    _selector_loops.clear()\n\n\natexit.register(_atexit_callback)\n\n\nclass BaseAsyncIOLoop(IOLoop):\n    def initialize(  # type: ignore\n        self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any\n    ) -> None:\n        # asyncio_loop is always the real underlying IOLoop. This is used in\n        # ioloop.py to maintain the asyncio-to-ioloop mappings.\n        self.asyncio_loop = asyncio_loop\n        # selector_loop is an event loop that implements the add_reader family of\n        # methods. Usually the same as asyncio_loop but differs on platforms such\n        # as windows where the default event loop does not implement these methods.\n        self.selector_loop = asyncio_loop\n        if hasattr(asyncio, \"ProactorEventLoop\") and isinstance(\n            asyncio_loop, asyncio.ProactorEventLoop  # type: ignore\n        ):\n            # Ignore this line for mypy because the abstract method checker\n            # doesn't understand dynamic proxies.\n            self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop)  # type: ignore\n        # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)\n        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]\n        # Set of fds listening for reads/writes\n        self.readers = set()  # type: Set[int]\n        self.writers = set()  # type: Set[int]\n        self.closing = False\n        # If an asyncio loop was closed through an asyncio interface\n        # instead of IOLoop.close(), we'd never hear about it and may\n        # have left a dangling reference in our map. In case an\n        # application (or, more likely, a test suite) creates and\n        # destroys a lot of event loops in this way, check here to\n        # ensure that we don't have a lot of dead loops building up in\n        # the map.\n        #\n        # TODO(bdarnell): consider making self.asyncio_loop a weakref\n        # for AsyncIOMainLoop and make _ioloop_for_asyncio a\n        # WeakKeyDictionary.\n        for loop in list(IOLoop._ioloop_for_asyncio):\n            if loop.is_closed():\n                del IOLoop._ioloop_for_asyncio[loop]\n        IOLoop._ioloop_for_asyncio[asyncio_loop] = self\n\n        self._thread_identity = 0\n\n        super().initialize(**kwargs)\n\n        def assign_thread_identity() -> None:\n            self._thread_identity = threading.get_ident()\n\n        self.add_callback(assign_thread_identity)\n\n    def close(self, all_fds: bool = False) -> None:\n        self.closing = True\n        for fd in list(self.handlers):\n            fileobj, handler_func = self.handlers[fd]\n            self.remove_handler(fd)\n            if all_fds:\n                self.close_fd(fileobj)\n        # Remove the mapping before closing the asyncio loop. If this\n        # happened in the other order, we could race against another\n        # initialize() call which would see the closed asyncio loop,\n        # assume it was closed from the asyncio side, and do this\n        # cleanup for us, leading to a KeyError.\n        del IOLoop._ioloop_for_asyncio[self.asyncio_loop]\n        if self.selector_loop is not self.asyncio_loop:\n            self.selector_loop.close()\n        self.asyncio_loop.close()\n\n    def add_handler(\n        self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int\n    ) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd in self.handlers:\n            raise ValueError(\"fd %s added twice\" % fd)\n        self.handlers[fd] = (fileobj, handler)\n        if events & IOLoop.READ:\n            self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n            self.readers.add(fd)\n        if events & IOLoop.WRITE:\n            self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n            self.writers.add(fd)\n\n    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if events & IOLoop.READ:\n            if fd not in self.readers:\n                self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n                self.readers.add(fd)\n        else:\n            if fd in self.readers:\n                self.selector_loop.remove_reader(fd)\n                self.readers.remove(fd)\n        if events & IOLoop.WRITE:\n            if fd not in self.writers:\n                self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n                self.writers.add(fd)\n        else:\n            if fd in self.writers:\n                self.selector_loop.remove_writer(fd)\n                self.writers.remove(fd)\n\n    def remove_handler(self, fd: Union[int, _Selectable]) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd not in self.handlers:\n            return\n        if fd in self.readers:\n            self.selector_loop.remove_reader(fd)\n            self.readers.remove(fd)\n        if fd in self.writers:\n            self.selector_loop.remove_writer(fd)\n            self.writers.remove(fd)\n        del self.handlers[fd]\n\n    def _handle_events(self, fd: int, events: int) -> None:\n        fileobj, handler_func = self.handlers[fd]\n        handler_func(fileobj, events)\n\n    def start(self) -> None:\n        try:\n            old_loop = asyncio.get_event_loop()\n        except (RuntimeError, AssertionError):\n            old_loop = None  # type: ignore\n        try:\n            self._setup_logging()\n            asyncio.set_event_loop(self.asyncio_loop)\n            self.asyncio_loop.run_forever()\n        finally:\n            asyncio.set_event_loop(old_loop)\n\n    def stop(self) -> None:\n        self.asyncio_loop.stop()\n\n    def call_at(\n        self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any\n    ) -> object:\n        # asyncio.call_at supports *args but not **kwargs, so bind them here.\n        # We do not synchronize self.time and asyncio_loop.time, so\n        # convert from absolute to relative.\n        return self.asyncio_loop.call_later(\n            max(0, when - self.time()),\n            self._run_callback,\n            functools.partial(callback, *args, **kwargs),\n        )\n\n    def remove_timeout(self, timeout: object) -> None:\n        timeout.cancel()  # type: ignore\n\n    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:\n        if threading.get_ident() == self._thread_identity:\n            call_soon = self.asyncio_loop.call_soon\n        else:\n            call_soon = self.asyncio_loop.call_soon_threadsafe\n        try:\n            call_soon(self._run_callback, functools.partial(callback, *args, **kwargs))\n        except RuntimeError:\n            # \"Event loop is closed\". Swallow the exception for\n            # consistency with PollIOLoop (and logical consistency\n            # with the fact that we can't guarantee that an\n            # add_callback that completes without error will\n            # eventually execute).\n            pass\n        except AttributeError:\n            # ProactorEventLoop may raise this instead of RuntimeError\n            # if call_soon_threadsafe races with a call to close().\n            # Swallow it too for consistency.\n            pass\n\n    def add_callback_from_signal(\n        self, callback: Callable, *args: Any, **kwargs: Any\n    ) -> None:\n        try:\n            self.asyncio_loop.call_soon_threadsafe(\n                self._run_callback, functools.partial(callback, *args, **kwargs)\n            )\n        except RuntimeError:\n            pass\n\n    def run_in_executor(\n        self,\n        executor: Optional[concurrent.futures.Executor],\n        func: Callable[..., _T],\n        *args: Any\n    ) -> Awaitable[_T]:\n        return self.asyncio_loop.run_in_executor(executor, func, *args)\n\n    def set_default_executor(self, executor: concurrent.futures.Executor) -> None:\n        return self.asyncio_loop.set_default_executor(executor)\n\n\nclass AsyncIOMainLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the\n    current ``asyncio`` event loop (i.e. the one returned by\n    ``asyncio.get_event_loop()``).\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n\n    .. versionchanged:: 5.0\n\n       Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        super().initialize(asyncio.get_event_loop(), **kwargs)\n\n    def make_current(self) -> None:\n        # AsyncIOMainLoop already refers to the current asyncio loop so\n        # nothing to do here.\n        pass\n\n\nclass AsyncIOLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOLoop`` is an `.IOLoop` that runs on an ``asyncio`` event loop.\n    This class follows the usual Tornado semantics for creating new\n    ``IOLoops``; these loops are not necessarily related to the\n    ``asyncio`` default event loop.\n\n    Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object\n    can be accessed with the ``asyncio_loop`` attribute.\n\n    .. versionchanged:: 5.0\n\n       When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets\n       the current `asyncio` event loop.\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        self.is_current = False\n        loop = asyncio.new_event_loop()\n        try:\n            super().initialize(loop, **kwargs)\n        except Exception:\n            # If initialize() does not succeed (taking ownership of the loop),\n            # we have to close it.\n            loop.close()\n            raise\n\n    def close(self, all_fds: bool = False) -> None:\n        if self.is_current:\n            self.clear_current()\n        super().close(all_fds=all_fds)\n\n    def make_current(self) -> None:\n        if not self.is_current:\n            try:\n                self.old_asyncio = asyncio.get_event_loop()\n            except (RuntimeError, AssertionError):\n                self.old_asyncio = None  # type: ignore\n            self.is_current = True\n        asyncio.set_event_loop(self.asyncio_loop)\n\n    def _clear_current_hook(self) -> None:\n        if self.is_current:\n            asyncio.set_event_loop(self.old_asyncio)\n            self.is_current = False\n\n\ndef to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert an `asyncio.Future` to a `tornado.concurrent.Future`.\n\n    .. versionadded:: 4.1\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now a no-op.\n    \"\"\"\n    return asyncio_future\n\n\ndef to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert a Tornado yieldable object to an `asyncio.Future`.\n\n    .. versionadded:: 4.1\n\n    .. versionchanged:: 4.3\n       Now accepts any yieldable object, not just\n       `tornado.concurrent.Future`.\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now equivalent to `tornado.gen.convert_yielded`.\n    \"\"\"\n    return convert_yielded(tornado_future)\n\n\nif sys.platform == \"win32\" and hasattr(asyncio, \"WindowsSelectorEventLoopPolicy\"):\n    # \"Any thread\" and \"selector\" should be orthogonal, but there's not a clean\n    # interface for composing policies so pick the right base.\n    _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy  # type: ignore\nelse:\n    _BasePolicy = asyncio.DefaultEventLoopPolicy\n\n\nclass AnyThreadEventLoopPolicy(_BasePolicy):  # type: ignore\n    \"\"\"Event loop policy that allows loop creation on any thread.\n\n    The default `asyncio` event loop policy only automatically creates\n    event loops in the main threads. Other threads must create event\n    loops explicitly or `asyncio.get_event_loop` (and therefore\n    `.IOLoop.current`) will fail. Installing this policy allows event\n    loops to be created automatically on any thread, matching the\n    behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).\n\n    Usage::\n\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n\n    .. versionadded:: 5.0\n\n    \"\"\"\n\n    def get_event_loop(self) -> asyncio.AbstractEventLoop:\n        try:\n            return super().get_event_loop()\n        except (RuntimeError, AssertionError):\n            # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie)\n            # and changed to a RuntimeError in 3.4.3.\n            # \"There is no current event loop in thread %r\"\n            loop = self.new_event_loop()\n            self.set_event_loop(loop)\n            return loop\n\n\nclass SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()\n\n\nclass AddThreadSelectorEventLoop(asyncio.AbstractEventLoop):\n    \"\"\"Wrap an event loop to add implementations of the ``add_reader`` method family.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user; all callbacks are\n    run on the wrapped event loop's thread.\n\n    This class is used automatically by Tornado; applications should not need\n    to refer to it directly.\n\n    It is safe to wrap any event loop with this class, although it only makes sense\n    for event loops that do not implement the ``add_reader`` family of methods\n    themselves (i.e. ``WindowsProactorEventLoop``)\n\n    Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop.\n\n    \"\"\"\n\n    # This class is a __getattribute__-based proxy. All attributes other than those\n    # in this set are proxied through to the underlying loop.\n    MY_ATTRIBUTES = {\n        \"_real_loop\",\n        \"_selector\",\n        \"add_reader\",\n        \"add_writer\",\n        \"close\",\n        \"remove_reader\",\n        \"remove_writer\",\n    }\n\n    def __getattribute__(self, name: str) -> Any:\n        if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES:\n            return super().__getattribute__(name)\n        return getattr(self._real_loop, name)\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n        self._selector = SelectorThread(real_loop)\n\n    def close(self) -> None:\n        self._selector.close()\n        self._real_loop.close()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_reader(fd, callback, *args)\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_writer(fd, callback, *args)\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_reader(fd)\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_writer(fd)\n",
            "new_file_content": "\"\"\"Bridges between the `asyncio` module and Tornado IOLoop.\n\n.. versionadded:: 3.2\n\nThis module integrates Tornado with the ``asyncio`` module introduced\nin Python 3.4. This makes it possible to combine the two libraries on\nthe same event loop.\n\n.. deprecated:: 5.0\n\n   While the code in this module is still used, it is now enabled\n   automatically when `asyncio` is available, so applications should\n   no longer need to refer to this module directly.\n\n.. note::\n\n   Tornado is designed to use a selector-based event loop. On Windows,\n   where a proactor-based event loop has been the default since Python 3.8,\n   a selector event loop is emulated by running ``select`` on a separate thread.\n   Configuring ``asyncio`` to use a selector event loop may improve performance\n   of Tornado (but may reduce performance of other ``asyncio``-based libraries\n   in the same process).\n\"\"\"\n\nimport asyncio\nimport atexit\nimport concurrent.futures\nimport errno\nimport functools\nimport select\nimport socket\nimport sys\nimport threading\nimport typing\nfrom tornado.gen import convert_yielded\nfrom tornado.ioloop import IOLoop, _Selectable\n\nfrom typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict\n\nif typing.TYPE_CHECKING:\n    from typing import Set  # noqa: F401\n    from typing_extensions import Protocol\n\n    class _HasFileno(Protocol):\n        def fileno(self) -> int:\n            pass\n\n    _FileDescriptorLike = Union[int, _HasFileno]\n\n_T = TypeVar(\"_T\")\n\n\n# Collection of selector thread event loops to shut down on exit.\n_selector_loops = set()  # type: Set[SelectorThread]\n\n\ndef _atexit_callback() -> None:\n    for loop in _selector_loops:\n        with loop._select_cond:\n            loop._closing_selector = True\n            loop._select_cond.notify()\n        try:\n            loop._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n        # If we don't join our (daemon) thread here, we may get a deadlock\n        # during interpreter shutdown. I don't really understand why. This\n        # deadlock happens every time in CI (both travis and appveyor) but\n        # I've never been able to reproduce locally.\n        loop._thread.join()\n    _selector_loops.clear()\n\n\natexit.register(_atexit_callback)\n\n\nclass BaseAsyncIOLoop(IOLoop):\n    def initialize(  # type: ignore\n        self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any\n    ) -> None:\n        # asyncio_loop is always the real underlying IOLoop. This is used in\n        # ioloop.py to maintain the asyncio-to-ioloop mappings.\n        self.asyncio_loop = asyncio_loop\n        # selector_loop is an event loop that implements the add_reader family of\n        # methods. Usually the same as asyncio_loop but differs on platforms such\n        # as windows where the default event loop does not implement these methods.\n        self.selector_loop = asyncio_loop\n        if hasattr(asyncio, \"ProactorEventLoop\") and isinstance(\n            asyncio_loop, asyncio.ProactorEventLoop  # type: ignore\n        ):\n            # Ignore this line for mypy because the abstract method checker\n            # doesn't understand dynamic proxies.\n            self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop)  # type: ignore\n        # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)\n        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]\n        # Set of fds listening for reads/writes\n        self.readers = set()  # type: Set[int]\n        self.writers = set()  # type: Set[int]\n        self.closing = False\n        # If an asyncio loop was closed through an asyncio interface\n        # instead of IOLoop.close(), we'd never hear about it and may\n        # have left a dangling reference in our map. In case an\n        # application (or, more likely, a test suite) creates and\n        # destroys a lot of event loops in this way, check here to\n        # ensure that we don't have a lot of dead loops building up in\n        # the map.\n        #\n        # TODO(bdarnell): consider making self.asyncio_loop a weakref\n        # for AsyncIOMainLoop and make _ioloop_for_asyncio a\n        # WeakKeyDictionary.\n        for loop in list(IOLoop._ioloop_for_asyncio):\n            if loop.is_closed():\n                del IOLoop._ioloop_for_asyncio[loop]\n        IOLoop._ioloop_for_asyncio[asyncio_loop] = self\n\n        self._thread_identity = 0\n\n        super().initialize(**kwargs)\n\n        def assign_thread_identity() -> None:\n            self._thread_identity = threading.get_ident()\n\n        self.add_callback(assign_thread_identity)\n\n    def close(self, all_fds: bool = False) -> None:\n        self.closing = True\n        for fd in list(self.handlers):\n            fileobj, handler_func = self.handlers[fd]\n            self.remove_handler(fd)\n            if all_fds:\n                self.close_fd(fileobj)\n        # Remove the mapping before closing the asyncio loop. If this\n        # happened in the other order, we could race against another\n        # initialize() call which would see the closed asyncio loop,\n        # assume it was closed from the asyncio side, and do this\n        # cleanup for us, leading to a KeyError.\n        del IOLoop._ioloop_for_asyncio[self.asyncio_loop]\n        if self.selector_loop is not self.asyncio_loop:\n            self.selector_loop.close()\n        self.asyncio_loop.close()\n\n    def add_handler(\n        self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int\n    ) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd in self.handlers:\n            raise ValueError(\"fd %s added twice\" % fd)\n        self.handlers[fd] = (fileobj, handler)\n        if events & IOLoop.READ:\n            self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n            self.readers.add(fd)\n        if events & IOLoop.WRITE:\n            self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n            self.writers.add(fd)\n\n    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if events & IOLoop.READ:\n            if fd not in self.readers:\n                self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n                self.readers.add(fd)\n        else:\n            if fd in self.readers:\n                self.selector_loop.remove_reader(fd)\n                self.readers.remove(fd)\n        if events & IOLoop.WRITE:\n            if fd not in self.writers:\n                self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n                self.writers.add(fd)\n        else:\n            if fd in self.writers:\n                self.selector_loop.remove_writer(fd)\n                self.writers.remove(fd)\n\n    def remove_handler(self, fd: Union[int, _Selectable]) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd not in self.handlers:\n            return\n        if fd in self.readers:\n            self.selector_loop.remove_reader(fd)\n            self.readers.remove(fd)\n        if fd in self.writers:\n            self.selector_loop.remove_writer(fd)\n            self.writers.remove(fd)\n        del self.handlers[fd]\n\n    def _handle_events(self, fd: int, events: int) -> None:\n        fileobj, handler_func = self.handlers[fd]\n        handler_func(fileobj, events)\n\n    def start(self) -> None:\n        try:\n            old_loop = asyncio.get_event_loop()\n        except (RuntimeError, AssertionError):\n            old_loop = None  # type: ignore\n        try:\n            self._setup_logging()\n            asyncio.set_event_loop(self.asyncio_loop)\n            self.asyncio_loop.run_forever()\n        finally:\n            asyncio.set_event_loop(old_loop)\n\n    def stop(self) -> None:\n        self.asyncio_loop.stop()\n\n    def call_at(\n        self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any\n    ) -> object:\n        # asyncio.call_at supports *args but not **kwargs, so bind them here.\n        # We do not synchronize self.time and asyncio_loop.time, so\n        # convert from absolute to relative.\n        return self.asyncio_loop.call_later(\n            max(0, when - self.time()),\n            self._run_callback,\n            functools.partial(callback, *args, **kwargs),\n        )\n\n    def remove_timeout(self, timeout: object) -> None:\n        timeout.cancel()  # type: ignore\n\n    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:\n        if threading.get_ident() == self._thread_identity:\n            call_soon = self.asyncio_loop.call_soon\n        else:\n            call_soon = self.asyncio_loop.call_soon_threadsafe\n        try:\n            call_soon(self._run_callback, functools.partial(callback, *args, **kwargs))\n        except RuntimeError:\n            # \"Event loop is closed\". Swallow the exception for\n            # consistency with PollIOLoop (and logical consistency\n            # with the fact that we can't guarantee that an\n            # add_callback that completes without error will\n            # eventually execute).\n            pass\n        except AttributeError:\n            # ProactorEventLoop may raise this instead of RuntimeError\n            # if call_soon_threadsafe races with a call to close().\n            # Swallow it too for consistency.\n            pass\n\n    def add_callback_from_signal(\n        self, callback: Callable, *args: Any, **kwargs: Any\n    ) -> None:\n        try:\n            self.asyncio_loop.call_soon_threadsafe(\n                self._run_callback, functools.partial(callback, *args, **kwargs)\n            )\n        except RuntimeError:\n            pass\n\n    def run_in_executor(\n        self,\n        executor: Optional[concurrent.futures.Executor],\n        func: Callable[..., _T],\n        *args: Any\n    ) -> Awaitable[_T]:\n        return self.asyncio_loop.run_in_executor(executor, func, *args)\n\n    def set_default_executor(self, executor: concurrent.futures.Executor) -> None:\n        return self.asyncio_loop.set_default_executor(executor)\n\n\nclass AsyncIOMainLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the\n    current ``asyncio`` event loop (i.e. the one returned by\n    ``asyncio.get_event_loop()``).\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n\n    .. versionchanged:: 5.0\n\n       Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        super().initialize(asyncio.get_event_loop(), **kwargs)\n\n    def make_current(self) -> None:\n        # AsyncIOMainLoop already refers to the current asyncio loop so\n        # nothing to do here.\n        pass\n\n\nclass AsyncIOLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOLoop`` is an `.IOLoop` that runs on an ``asyncio`` event loop.\n    This class follows the usual Tornado semantics for creating new\n    ``IOLoops``; these loops are not necessarily related to the\n    ``asyncio`` default event loop.\n\n    Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object\n    can be accessed with the ``asyncio_loop`` attribute.\n\n    .. versionchanged:: 5.0\n\n       When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets\n       the current `asyncio` event loop.\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        self.is_current = False\n        loop = asyncio.new_event_loop()\n        try:\n            super().initialize(loop, **kwargs)\n        except Exception:\n            # If initialize() does not succeed (taking ownership of the loop),\n            # we have to close it.\n            loop.close()\n            raise\n\n    def close(self, all_fds: bool = False) -> None:\n        if self.is_current:\n            self.clear_current()\n        super().close(all_fds=all_fds)\n\n    def make_current(self) -> None:\n        if not self.is_current:\n            try:\n                self.old_asyncio = asyncio.get_event_loop()\n            except (RuntimeError, AssertionError):\n                self.old_asyncio = None  # type: ignore\n            self.is_current = True\n        asyncio.set_event_loop(self.asyncio_loop)\n\n    def _clear_current_hook(self) -> None:\n        if self.is_current:\n            asyncio.set_event_loop(self.old_asyncio)\n            self.is_current = False\n\n\ndef to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert an `asyncio.Future` to a `tornado.concurrent.Future`.\n\n    .. versionadded:: 4.1\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now a no-op.\n    \"\"\"\n    return asyncio_future\n\n\ndef to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert a Tornado yieldable object to an `asyncio.Future`.\n\n    .. versionadded:: 4.1\n\n    .. versionchanged:: 4.3\n       Now accepts any yieldable object, not just\n       `tornado.concurrent.Future`.\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now equivalent to `tornado.gen.convert_yielded`.\n    \"\"\"\n    return convert_yielded(tornado_future)\n\n\nif sys.platform == \"win32\" and hasattr(asyncio, \"WindowsSelectorEventLoopPolicy\"):\n    # \"Any thread\" and \"selector\" should be orthogonal, but there's not a clean\n    # interface for composing policies so pick the right base.\n    _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy  # type: ignore\nelse:\n    _BasePolicy = asyncio.DefaultEventLoopPolicy\n\n\nclass AnyThreadEventLoopPolicy(_BasePolicy):  # type: ignore\n    \"\"\"Event loop policy that allows loop creation on any thread.\n\n    The default `asyncio` event loop policy only automatically creates\n    event loops in the main threads. Other threads must create event\n    loops explicitly or `asyncio.get_event_loop` (and therefore\n    `.IOLoop.current`) will fail. Installing this policy allows event\n    loops to be created automatically on any thread, matching the\n    behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).\n\n    Usage::\n\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n\n    .. versionadded:: 5.0\n\n    \"\"\"\n\n    def get_event_loop(self) -> asyncio.AbstractEventLoop:\n        try:\n            return super().get_event_loop()\n        except (RuntimeError, AssertionError):\n            # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie)\n            # and changed to a RuntimeError in 3.4.3.\n            # \"There is no current event loop in thread %r\"\n            loop = self.new_event_loop()\n            self.set_event_loop(loop)\n            return loop\n\n\nclass SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()\n\n\nclass AddThreadSelectorEventLoop(asyncio.AbstractEventLoop):\n    \"\"\"Wrap an event loop to add implementations of the ``add_reader`` method family.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user; all callbacks are\n    run on the wrapped event loop's thread.\n\n    This class is used automatically by Tornado; applications should not need\n    to refer to it directly.\n\n    It is safe to wrap any event loop with this class, although it only makes sense\n    for event loops that do not implement the ``add_reader`` family of methods\n    themselves (i.e. ``WindowsProactorEventLoop``)\n\n    Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop.\n\n    \"\"\"\n\n    # This class is a __getattribute__-based proxy. All attributes other than those\n    # in this set are proxied through to the underlying loop.\n    MY_ATTRIBUTES = {\n        \"_real_loop\",\n        \"_selector\",\n        \"add_reader\",\n        \"add_writer\",\n        \"close\",\n        \"remove_reader\",\n        \"remove_writer\",\n    }\n\n    def __getattribute__(self, name: str) -> Any:\n        if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES:\n            return super().__getattribute__(name)\n        return getattr(self._real_loop, name)\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n        self._selector = SelectorThread(real_loop)\n\n    def close(self) -> None:\n        self._selector.close()\n        self._real_loop.close()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_reader(fd, callback, *args)\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_writer(fd, callback, *args)\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_reader(fd)\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_writer(fd)\n",
            "header": {
                "file": {
                    "path": "tornado/platform/asyncio.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "b07af3a9",
                "new_commit_hash": "3863e78f",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/platform/asyncio.py"
            },
            "plus_file": {
                "path": "b/tornado/platform/asyncio.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 412,
                            "length": 6
                        },
                        "new_range": {
                            "start": 412,
                            "length": 8
                        },
                        "section": "class SelectorThread:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    but can be attached to a running asyncio loop.",
                                "type": "context"
                            },
                            {
                                "content": "    \"\"\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    _closed = False",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "        self._real_loop = real_loop",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/platform/asyncio.py",
                            "type": "class",
                            "name": "SelectorThread",
                            "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 404,
                            "end_lineno": 582,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 456,
                            "length": 6
                        },
                        "new_range": {
                            "start": 458,
                            "length": 8
                        },
                        "section": "class SelectorThread:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        self._waker_w.close()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def close(self) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "        if self._closed:",
                                "type": "added"
                            },
                            {
                                "content": "            return",
                                "type": "added"
                            },
                            {
                                "content": "        with self._select_cond:",
                                "type": "context"
                            },
                            {
                                "content": "            self._closing_selector = True",
                                "type": "context"
                            },
                            {
                                "content": "            self._select_cond.notify()",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/platform/asyncio.py",
                            "type": "class",
                            "name": "SelectorThread",
                            "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 404,
                            "end_lineno": 582,
                            "parent": null
                        },
                        {
                            "file_name": "tornado/platform/asyncio.py",
                            "type": "method",
                            "name": "SelectorThread.close",
                            "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 460,
                            "end_lineno": 471,
                            "parent": {
                                "file_name": "tornado/platform/asyncio.py",
                                "type": "class",
                                "name": "SelectorThread",
                                "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 404,
                                "end_lineno": 582,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 464,
                            "length": 6
                        },
                        "new_range": {
                            "start": 468,
                            "length": 7
                        },
                        "section": "class SelectorThread:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        _selector_loops.discard(self)",
                                "type": "context"
                            },
                            {
                                "content": "        self._waker_r.close()",
                                "type": "context"
                            },
                            {
                                "content": "        self._waker_w.close()",
                                "type": "context"
                            },
                            {
                                "content": "        self._closed = True",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def _wake_selector(self) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "        try:",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/platform/asyncio.py",
                            "type": "class",
                            "name": "SelectorThread",
                            "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 404,
                            "end_lineno": 582,
                            "parent": null
                        },
                        {
                            "file_name": "tornado/platform/asyncio.py",
                            "type": "method",
                            "name": "SelectorThread.close",
                            "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 460,
                            "end_lineno": 471,
                            "parent": {
                                "file_name": "tornado/platform/asyncio.py",
                                "type": "class",
                                "name": "SelectorThread",
                                "content": "class SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    _closed = False\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        if self._closed:\n            return\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n        self._closed = True\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 404,
                                "end_lineno": 582,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nimport asyncio\nimport unittest\n\nfrom concurrent.futures import ThreadPoolExecutor\nfrom tornado import gen\nfrom tornado.ioloop import IOLoop\nfrom tornado.platform.asyncio import (\n    AsyncIOLoop,\n    to_asyncio_future,\n    AnyThreadEventLoopPolicy,\n)\nfrom tornado.testing import AsyncTestCase, gen_test\n\n\nclass AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n\nclass LeakTest(unittest.TestCase):\n    def setUp(self):\n        # Trigger a cleanup of the mapping so we start with a clean slate.\n        AsyncIOLoop().close()\n        # If we don't clean up after ourselves other tests may fail on\n        # py34.\n        self.orig_policy = asyncio.get_event_loop_policy()\n        asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())\n\n    def tearDown(self):\n        asyncio.get_event_loop().close()\n        asyncio.set_event_loop_policy(self.orig_policy)\n\n    def test_ioloop_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOLoop using Tornado interfaces.\n            loop = AsyncIOLoop()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        self.assertEqual(new_count, 0)\n\n    def test_asyncio_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOMainLoop using asyncio interfaces.\n            loop = asyncio.new_event_loop()\n            loop.call_soon(IOLoop.current)\n            loop.call_soon(loop.stop)\n            loop.run_forever()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        # Because the cleanup is run on new loop creation, we have one\n        # dangling entry in the map (but only one).\n        self.assertEqual(new_count, 1)\n\n\nclass AnyThreadEventLoopPolicyTest(unittest.TestCase):\n    def setUp(self):\n        self.orig_policy = asyncio.get_event_loop_policy()\n        self.executor = ThreadPoolExecutor(1)\n\n    def tearDown(self):\n        asyncio.set_event_loop_policy(self.orig_policy)\n        self.executor.shutdown()\n\n    def get_event_loop_on_thread(self):\n        def get_and_close_event_loop():\n            \"\"\"Get the event loop. Close it if one is returned.\n\n            Returns the (closed) event loop. This is a silly thing\n            to do and leaves the thread in a broken state, but it's\n            enough for this test. Closing the loop avoids resource\n            leak warnings.\n            \"\"\"\n            loop = asyncio.get_event_loop()\n            loop.close()\n            return loop\n\n        future = self.executor.submit(get_and_close_event_loop)\n        return future.result()\n\n    def run_policy_test(self, accessor, expected_type):\n        # With the default policy, non-main threads don't get an event\n        # loop.\n        self.assertRaises(\n            (RuntimeError, AssertionError), self.executor.submit(accessor).result\n        )\n        # Set the policy and we can get a loop.\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n        self.assertIsInstance(self.executor.submit(accessor).result(), expected_type)\n        # Clean up to silence leak warnings. Always use asyncio since\n        # IOLoop doesn't (currently) close the underlying loop.\n        self.executor.submit(lambda: asyncio.get_event_loop().close()).result()  # type: ignore\n\n    def test_asyncio_accessor(self):\n        self.run_policy_test(asyncio.get_event_loop, asyncio.AbstractEventLoop)\n\n    def test_tornado_accessor(self):\n        self.run_policy_test(IOLoop.current, IOLoop)\n",
            "new_file_content": "# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nimport asyncio\nimport unittest\n\nfrom concurrent.futures import ThreadPoolExecutor\nfrom tornado import gen\nfrom tornado.ioloop import IOLoop\nfrom tornado.platform.asyncio import (\n    AsyncIOLoop,\n    to_asyncio_future,\n    AnyThreadEventLoopPolicy,\n    AddThreadSelectorEventLoop,\n)\nfrom tornado.testing import AsyncTestCase, gen_test\n\n\nclass AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n    def test_add_thread_close_idempotent(self):\n        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())\n        loop.close()\n        loop.close()\n\n\nclass LeakTest(unittest.TestCase):\n    def setUp(self):\n        # Trigger a cleanup of the mapping so we start with a clean slate.\n        AsyncIOLoop().close()\n        # If we don't clean up after ourselves other tests may fail on\n        # py34.\n        self.orig_policy = asyncio.get_event_loop_policy()\n        asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())\n\n    def tearDown(self):\n        asyncio.get_event_loop().close()\n        asyncio.set_event_loop_policy(self.orig_policy)\n\n    def test_ioloop_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOLoop using Tornado interfaces.\n            loop = AsyncIOLoop()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        self.assertEqual(new_count, 0)\n\n    def test_asyncio_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOMainLoop using asyncio interfaces.\n            loop = asyncio.new_event_loop()\n            loop.call_soon(IOLoop.current)\n            loop.call_soon(loop.stop)\n            loop.run_forever()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        # Because the cleanup is run on new loop creation, we have one\n        # dangling entry in the map (but only one).\n        self.assertEqual(new_count, 1)\n\n\nclass AnyThreadEventLoopPolicyTest(unittest.TestCase):\n    def setUp(self):\n        self.orig_policy = asyncio.get_event_loop_policy()\n        self.executor = ThreadPoolExecutor(1)\n\n    def tearDown(self):\n        asyncio.set_event_loop_policy(self.orig_policy)\n        self.executor.shutdown()\n\n    def get_event_loop_on_thread(self):\n        def get_and_close_event_loop():\n            \"\"\"Get the event loop. Close it if one is returned.\n\n            Returns the (closed) event loop. This is a silly thing\n            to do and leaves the thread in a broken state, but it's\n            enough for this test. Closing the loop avoids resource\n            leak warnings.\n            \"\"\"\n            loop = asyncio.get_event_loop()\n            loop.close()\n            return loop\n\n        future = self.executor.submit(get_and_close_event_loop)\n        return future.result()\n\n    def run_policy_test(self, accessor, expected_type):\n        # With the default policy, non-main threads don't get an event\n        # loop.\n        self.assertRaises(\n            (RuntimeError, AssertionError), self.executor.submit(accessor).result\n        )\n        # Set the policy and we can get a loop.\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n        self.assertIsInstance(self.executor.submit(accessor).result(), expected_type)\n        # Clean up to silence leak warnings. Always use asyncio since\n        # IOLoop doesn't (currently) close the underlying loop.\n        self.executor.submit(lambda: asyncio.get_event_loop().close()).result()  # type: ignore\n\n    def test_asyncio_accessor(self):\n        self.run_policy_test(asyncio.get_event_loop, asyncio.AbstractEventLoop)\n\n    def test_tornado_accessor(self):\n        self.run_policy_test(IOLoop.current, IOLoop)\n",
            "header": {
                "file": {
                    "path": "tornado/test/asyncio_test.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "3f9f3389",
                "new_commit_hash": "59825e8f",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tornado/test/asyncio_test.py"
            },
            "plus_file": {
                "path": "b/tornado/test/asyncio_test.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 20,
                            "length": 6
                        },
                        "new_range": {
                            "start": 20,
                            "length": 7
                        },
                        "section": "from tornado.platform.asyncio import ("
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    AsyncIOLoop,",
                                "type": "context"
                            },
                            {
                                "content": "    to_asyncio_future,",
                                "type": "context"
                            },
                            {
                                "content": "    AnyThreadEventLoopPolicy,",
                                "type": "context"
                            },
                            {
                                "content": "    AddThreadSelectorEventLoop,",
                                "type": "added"
                            },
                            {
                                "content": ")",
                                "type": "context"
                            },
                            {
                                "content": "from tornado.testing import AsyncTestCase, gen_test",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tornado/test/asyncio_test.py",
                            "type": "statement",
                            "name": "",
                            "content": "from tornado.platform.asyncio import (\n    AsyncIOLoop,\n    to_asyncio_future,\n    AnyThreadEventLoopPolicy,\n    AddThreadSelectorEventLoop,\n)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 19,
                            "end_lineno": 24,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 107,
                            "length": 6
                        },
                        "new_range": {
                            "start": 108,
                            "length": 11
                        },
                        "section": "class AsyncIOLoopTest(AsyncTestCase):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            42,",
                                "type": "context"
                            },
                            {
                                "content": "        )",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    def test_add_thread_close_idempotent(self):",
                                "type": "added"
                            },
                            {
                                "content": "        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())",
                                "type": "added"
                            },
                            {
                                "content": "        loop.close()",
                                "type": "added"
                            },
                            {
                                "content": "        loop.close()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "class LeakTest(unittest.TestCase):",
                                "type": "context"
                            },
                            {
                                "content": "    def setUp(self):",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "tornado/test/asyncio_test.py",
                            "type": "class",
                            "name": "AsyncIOLoopTest",
                            "content": "class AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n    def test_add_thread_close_idempotent(self):\n        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())\n        loop.close()\n        loop.close()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 28,
                            "end_lineno": 114,
                            "parent": null
                        }
                    ],
                    "added_entities": [
                        {
                            "file_name": "tornado/test/asyncio_test.py",
                            "type": "method",
                            "name": "AsyncIOLoopTest.test_add_thread_close_idempotent",
                            "content": "class AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n    def test_add_thread_close_idempotent(self):\n        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())\n        loop.close()\n        loop.close()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 111,
                            "end_lineno": 114,
                            "parent": {
                                "file_name": "tornado/test/asyncio_test.py",
                                "type": "class",
                                "name": "AsyncIOLoopTest",
                                "content": "class AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n    def test_add_thread_close_idempotent(self):\n        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())\n        loop.close()\n        loop.close()",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 28,
                                "end_lineno": 114,
                                "parent": null
                            }
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f^",
    "new_commit_hash": "63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f",
    "commit_message": "Make AddThreadEventLoop.close()  idempotent\n\naccording to spec: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.close\n\nThe call to _wake_selector would fail with EBADF when close is called a second time",
    "commit_date": "2021-05-19T10:02:08+02:00",
    "metadata": {}
}
{
    "repo_name": "tornado",
    "new_commit_hash": "63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f",
    "test_file_codes": [
        "# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nimport asyncio\nimport unittest\n\nfrom concurrent.futures import ThreadPoolExecutor\nfrom tornado import gen\nfrom tornado.ioloop import IOLoop\nfrom tornado.platform.asyncio import (\n    AsyncIOLoop,\n    to_asyncio_future,\n    AnyThreadEventLoopPolicy,\n    AddThreadSelectorEventLoop,\n)\nfrom tornado.testing import AsyncTestCase, gen_test\n\n\nclass AsyncIOLoopTest(AsyncTestCase):\n    def get_new_ioloop(self):\n        io_loop = AsyncIOLoop()\n        return io_loop\n\n    def test_asyncio_callback(self):\n        # Basic test that the asyncio loop is set up correctly.\n        asyncio.get_event_loop().call_soon(self.stop)\n        self.wait()\n\n    @gen_test\n    def test_asyncio_future(self):\n        # Test that we can yield an asyncio future from a tornado coroutine.\n        # Without 'yield from', we must wrap coroutines in ensure_future,\n        # which was introduced during Python 3.4, deprecating the prior \"async\".\n        if hasattr(asyncio, \"ensure_future\"):\n            ensure_future = asyncio.ensure_future\n        else:\n            # async is a reserved word in Python 3.7\n            ensure_future = getattr(asyncio, \"async\")\n\n        x = yield ensure_future(\n            asyncio.get_event_loop().run_in_executor(None, lambda: 42)\n        )\n        self.assertEqual(x, 42)\n\n    @gen_test\n    def test_asyncio_yield_from(self):\n        @gen.coroutine\n        def f():\n            event_loop = asyncio.get_event_loop()\n            x = yield from event_loop.run_in_executor(None, lambda: 42)\n            return x\n\n        result = yield f()\n        self.assertEqual(result, 42)\n\n    def test_asyncio_adapter(self):\n        # This test demonstrates that when using the asyncio coroutine\n        # runner (i.e. run_until_complete), the to_asyncio_future\n        # adapter is needed. No adapter is needed in the other direction,\n        # as demonstrated by other tests in the package.\n        @gen.coroutine\n        def tornado_coroutine():\n            yield gen.moment\n            raise gen.Return(42)\n\n        async def native_coroutine_without_adapter():\n            return await tornado_coroutine()\n\n        async def native_coroutine_with_adapter():\n            return await to_asyncio_future(tornado_coroutine())\n\n        # Use the adapter, but two degrees from the tornado coroutine.\n        async def native_coroutine_with_adapter2():\n            return await to_asyncio_future(native_coroutine_without_adapter())\n\n        # Tornado supports native coroutines both with and without adapters\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_without_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter), 42)\n        self.assertEqual(self.io_loop.run_sync(native_coroutine_with_adapter2), 42)\n\n        # Asyncio only supports coroutines that yield asyncio-compatible\n        # Futures (which our Future is since 5.0).\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_without_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter()\n            ),\n            42,\n        )\n        self.assertEqual(\n            asyncio.get_event_loop().run_until_complete(\n                native_coroutine_with_adapter2()\n            ),\n            42,\n        )\n\n    def test_add_thread_close_idempotent(self):\n        loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())\n        loop.close()\n        loop.close()\n\n\nclass LeakTest(unittest.TestCase):\n    def setUp(self):\n        # Trigger a cleanup of the mapping so we start with a clean slate.\n        AsyncIOLoop().close()\n        # If we don't clean up after ourselves other tests may fail on\n        # py34.\n        self.orig_policy = asyncio.get_event_loop_policy()\n        asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())\n\n    def tearDown(self):\n        asyncio.get_event_loop().close()\n        asyncio.set_event_loop_policy(self.orig_policy)\n\n    def test_ioloop_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOLoop using Tornado interfaces.\n            loop = AsyncIOLoop()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        self.assertEqual(new_count, 0)\n\n    def test_asyncio_close_leak(self):\n        orig_count = len(IOLoop._ioloop_for_asyncio)\n        for i in range(10):\n            # Create and close an AsyncIOMainLoop using asyncio interfaces.\n            loop = asyncio.new_event_loop()\n            loop.call_soon(IOLoop.current)\n            loop.call_soon(loop.stop)\n            loop.run_forever()\n            loop.close()\n        new_count = len(IOLoop._ioloop_for_asyncio) - orig_count\n        # Because the cleanup is run on new loop creation, we have one\n        # dangling entry in the map (but only one).\n        self.assertEqual(new_count, 1)\n\n\nclass AnyThreadEventLoopPolicyTest(unittest.TestCase):\n    def setUp(self):\n        self.orig_policy = asyncio.get_event_loop_policy()\n        self.executor = ThreadPoolExecutor(1)\n\n    def tearDown(self):\n        asyncio.set_event_loop_policy(self.orig_policy)\n        self.executor.shutdown()\n\n    def get_event_loop_on_thread(self):\n        def get_and_close_event_loop():\n            \"\"\"Get the event loop. Close it if one is returned.\n\n            Returns the (closed) event loop. This is a silly thing\n            to do and leaves the thread in a broken state, but it's\n            enough for this test. Closing the loop avoids resource\n            leak warnings.\n            \"\"\"\n            loop = asyncio.get_event_loop()\n            loop.close()\n            return loop\n\n        future = self.executor.submit(get_and_close_event_loop)\n        return future.result()\n\n    def run_policy_test(self, accessor, expected_type):\n        # With the default policy, non-main threads don't get an event\n        # loop.\n        self.assertRaises(\n            (RuntimeError, AssertionError), self.executor.submit(accessor).result\n        )\n        # Set the policy and we can get a loop.\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n        self.assertIsInstance(self.executor.submit(accessor).result(), expected_type)\n        # Clean up to silence leak warnings. Always use asyncio since\n        # IOLoop doesn't (currently) close the underlying loop.\n        self.executor.submit(lambda: asyncio.get_event_loop().close()).result()  # type: ignore\n\n    def test_asyncio_accessor(self):\n        self.run_policy_test(asyncio.get_event_loop, asyncio.AbstractEventLoop)\n\n    def test_tornado_accessor(self):\n        self.run_policy_test(IOLoop.current, IOLoop)\n",
        "#!/usr/bin/env python\nimport unittest\nimport sys\nimport time\nimport traceback\nimport platform\nimport os\n\n\nclass PytestLikeResult(unittest.TestResult):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.start_time = None\n        # Store (test, outcome, info) for each test:\n        #   outcome ∈ { \"passed\", \"failed\", \"error\", \"skipped\" }\n        #   info can hold the exception or skip reason, etc.\n        self.results = []\n\n    def startTestRun(self):\n        \"\"\"Called once before all tests.\"\"\"\n        self.start_time = time.time()\n        # Print a header similar to pytest\n        print(\"=\" * 29 + \" test session starts \" + \"=\" * 29)\n        # You can customize the platform/versions/paths as you like:\n        print(\n            f\"platform {platform.system().lower()} -- Python {platform.python_version()}\"\n        )\n        print(f\"rootdir: {os.path.abspath(os.curdir)}\")\n\n    def stopTestRun(self):\n        \"\"\"Called once after all tests are done.\"\"\"\n        elapsed = time.time() - self.start_time\n        print()  # blank line after final dot\n\n        # Summarize\n        ntests = len(self.results)\n        print(f\"collected {ntests} items\")\n\n        # Show details of failures/errors\n        failures = [r for r in self.results if r[1] == \"failed\"]\n        errors = [r for r in self.results if r[1] == \"error\"]\n\n        if failures:\n            print(\"\\n\" + \"=\" * 35 + \" FAILURES \" + \"=\" * 35)\n            for test, outcome, err_info in failures:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n        if errors:\n            print(\"\\n\" + \"=\" * 35 + \" ERRORS \" + \"=\" * 37)\n            for test, outcome, err_info in errors:\n                test = test.split(\"::\")\n                if len(test) == 3:\n                    test = f\"{test[1]}.{test[2]}\"\n                else:\n                    test = f\"{test[1]}\"\n                print(f\"__________ {test} __________\")\n                self._print_traceback(err_info)\n\n        # Optional short summary lines for passes/fails/skips\n        print(\"\\n==================== short test summary info ====================\")\n        for test, outcome, info in self.results:\n            if outcome in (\"passed\", \"failed\", \"error\", \"skipped\"):\n                print(f\"{outcome.upper()} {test}\")\n\n        # Final one-line summary: “1 failed, 1 errors, 10 passed in 2.04s”\n        passed_count = sum(1 for r in self.results if r[1] == \"passed\")\n        failed_count = len(failures)\n        error_count = len(errors)\n        skipped_count = sum(1 for r in self.results if r[1] == \"skipped\")\n\n        summary_bits = []\n        if failed_count:\n            summary_bits.append(f\"{failed_count} failed\")\n        if error_count:\n            summary_bits.append(f\"{error_count} error{'s' if error_count != 1 else ''}\")\n        if passed_count:\n            summary_bits.append(f\"{passed_count} passed\")\n        if skipped_count:\n            summary_bits.append(f\"{skipped_count} skipped\")\n\n        summary_str = \", \".join(summary_bits) if summary_bits else \"no tests run\"\n        print(\n            f\"=================== {summary_str} in {elapsed:.2f}s ===================\"\n        )\n\n    def startTest(self, test):\n        \"\"\"Called right before each test method.\"\"\"\n        super().startTest(test)\n        # Print a dot or some indicator\n        print(\".\", end=\"\", flush=True)\n\n    def addSuccess(self, test):\n        super().addSuccess(test)\n        self.results.append((self._test_id(test), \"passed\", None))\n\n    def addFailure(self, test, err):\n        super().addFailure(test, err)\n        self.results.append((self._test_id(test), \"failed\", err))\n\n    def addError(self, test, err):\n        super().addError(test, err)\n        self.results.append((self._test_id(test), \"error\", err))\n\n    def addSkip(self, test, reason):\n        super().addSkip(test, reason)\n        self.results.append((self._test_id(test), \"skipped\", reason))\n\n    def _print_traceback(self, err_info):\n        \"\"\"Helper to print the traceback like pytest does.\"\"\"\n        if isinstance(err_info, tuple) and len(err_info) == 3:\n            exc_type, exc_value, tb = err_info\n            traceback.print_exception(exc_type, exc_value, tb, file=sys.stdout)\n        else:\n            print(str(err_info))\n\n    def _test_id(self, test):\n        try:\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}::{test._testMethodName}\"\n        except AttributeError:\n            # Fallback for _ErrorHolder objects\n            return f\"{test.__class__.__module__}::{test.__class__.__name__}\"\n\n\nclass PytestLikeRunner(unittest.TextTestRunner):\n    \"\"\"A custom TextTestRunner that uses the PytestLikeResult.\"\"\"\n\n    resultclass = PytestLikeResult\n\n    def run(self, test):\n        result = self._makeResult()\n        # Overriding run to ensure we call startTestRun/stopTestRun\n        result.startTestRun()\n        test(result)\n        result.stopTestRun()\n        return result\n\n    def tearDown(self):\n        # Reset signal handlers\n        from twisted.internet import reactor\n\n        reactor.removeAll()\n        reactor.stop()\n        super().tearDown()\n\n\ndef main():\n    loader = unittest.TestLoader()\n    suite = loader.discover(\".\", pattern=\"test_*.py\")  # discover tests in r2e_tests/\n    runner = PytestLikeRunner(verbosity=0)\n    result = runner.run(suite)\n    # Exit with code 0 if all tests passed/skipped, 1 if there were fails/errors\n    sys.exit(not result.wasSuccessful())\n\n\nif __name__ == \"__main__\":\n    main()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "tornado_unittest_runner.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 677ms\nPrepared 1 package in 1.00s\nInstalled 1 package in 0.76ms\n + tornado==6.2.dev1 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f)\nResolved 29 packages in 9ms\nInstalled 29 packages in 48ms\n + attrs==24.3.0\n + automat==24.8.1\n + cachetools==5.5.0\n + cffi==1.17.1\n + chardet==5.2.0\n + colorama==0.4.6\n + constantly==23.10.4\n + distlib==0.3.9\n + exceptiongroup==1.2.2\n + filelock==3.16.1\n + hyperlink==21.0.0\n + idna==3.10\n + incremental==24.7.2\n + iniconfig==2.0.0\n + packaging==24.2\n + platformdirs==4.3.6\n + pluggy==1.5.0\n + pycares==4.5.0\n + pycparser==2.22\n + pycurl==7.45.4\n + pyproject-api==1.8.0\n + pytest==8.3.4\n + setuptools==75.6.0\n + tomli==2.2.1\n + tox==4.23.2\n + twisted==24.11.0\n + typing-extensions==4.12.2\n + virtualenv==20.28.0\n + zope-interface==7.2\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f\n.........\ncollected 9 items\n\n==================== short test summary info ====================\nPASSED r2e_tests.test_1::AnyThreadEventLoopPolicyTest::test_asyncio_accessor\nPASSED r2e_tests.test_1::AnyThreadEventLoopPolicyTest::test_tornado_accessor\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_add_thread_close_idempotent\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_adapter\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_callback\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_future\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_yield_from\nPASSED r2e_tests.test_1::LeakTest::test_asyncio_close_leak\nPASSED r2e_tests.test_1::LeakTest::test_ioloop_close_leak\n=================== 9 passed in 0.01s ===================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts =============================\nplatform linux -- Python 3.9.21\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f\n.........\ncollected 9 items\n\n=================================== ERRORS =====================================\n__________ AsyncIOLoopTest.test_add_thread_close_idempotent __________\nTraceback (most recent call last):\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 59, in testPartExecutor\n    yield\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 592, in run\n    self._callTestMethod(testMethod)\n  File \"/home/gcpuser/.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py\", line 550, in _callTestMethod\n    method()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f/tornado/testing.py\", line 100, in __call__\n    result = self.orig_method(*args, **kwargs)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f/r2e_tests/test_1.py\", line 114, in test_add_thread_close_idempotent\n    loop.close()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f/tornado/platform/asyncio.py\", line 620, in close\n    self._selector.close()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f/tornado/platform/asyncio.py\", line 462, in close\n    self._wake_selector()\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/tornado_63ae59dda4bc3d83f7a1265f3c17807d4bc7e78f/tornado/platform/asyncio.py\", line 470, in _wake_selector\n    self._waker_w.send(b\"a\")\nOSError: [Errno 9] Bad file descriptor\n\n==================== short test summary info ====================\nPASSED r2e_tests.test_1::AnyThreadEventLoopPolicyTest::test_asyncio_accessor\nPASSED r2e_tests.test_1::AnyThreadEventLoopPolicyTest::test_tornado_accessor\nERROR r2e_tests.test_1::AsyncIOLoopTest::test_add_thread_close_idempotent\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_adapter\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_callback\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_future\nPASSED r2e_tests.test_1::AsyncIOLoopTest::test_asyncio_yield_from\nPASSED r2e_tests.test_1::LeakTest::test_asyncio_close_leak\nPASSED r2e_tests.test_1::LeakTest::test_ioloop_close_leak\n=================== 1 error, 8 passed in 0.01s ===================\n",
    "old_commit_res_stderr": ""
}
tornado/platform/asyncio.py tornado/test/asyncio_test.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Calling `close()` Multiple Times on `AddThreadSelectorEventLoop` Raises OSError

**Description:**
When the `close()` method is invoked more than once on an instance of `AddThreadSelectorEventLoop`, the second call fails with an `OSError`. This behavior prevents the method from being idempotent, which is unexpected and can lead to runtime errors in applications that attempt to close the event loop multiple times.

**Example Code:**
```python
loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())
loop.close()
loop.close()
```

**Expected Behavior:**
Calling `close()` multiple times on the same `AddThreadSelectorEventLoop` instance should have no adverse effects after the first call. Subsequent calls should be safely ignored without raising any exceptions.

**Actual Behavior:**
The second call to `close()` raises an `OSError` with the message `[Errno 9] Bad file descriptor`, causing the application to crash unexpectedly.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/platform/asyncio.py
"""Bridges between the `asyncio` module and Tornado IOLoop.

.. versionadded:: 3.2

This module integrates Tornado with the ``asyncio`` module introduced
in Python 3.4. This makes it possible to combine the two libraries on
the same event loop.

.. deprecated:: 5.0

   While the code in this module is still used, it is now enabled
   automatically when `asyncio` is available, so applications should
   no longer need to refer to this module directly.

.. note::

   Tornado is designed to use a selector-based event loop. On Windows,
   where a proactor-based event loop has been the default since Python 3.8,
   a selector event loop is emulated by running ``select`` on a separate thread.
   Configuring ``asyncio`` to use a selector event loop may improve performance
   of Tornado (but may reduce performance of other ``asyncio``-based libraries
   in the same process).
"""

import asyncio
import atexit
import concurrent.futures
import errno
import functools
import select
import socket
import sys
import threading
import typing
from tornado.gen import convert_yielded
from tornado.ioloop import IOLoop, _Selectable

from typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict

if typing.TYPE_CHECKING:
    from typing import Set  # noqa: F401
    from typing_extensions import Protocol

    class _HasFileno(Protocol):
        def fileno(self) -> int:
            pass

    _FileDescriptorLike = Union[int, _HasFileno]

_T = TypeVar("_T")


# Collection of selector thread event loops to shut down on exit.
_selector_loops = set()  # type: Set[SelectorThread]


def _atexit_callback() -> None:
    for loop in _selector_loops:
        with loop._select_cond:
            loop._closing_selector = True
            loop._select_cond.notify()
        try:
            loop._waker_w.send(b"a")
        except BlockingIOError:
            pass
        # If we don't join our (daemon) thread here, we may get a deadlock
        # during interpreter shutdown. I don't really understand why. This
        # deadlock happens every time in CI (both travis and appveyor) but
        # I've never been able to reproduce locally.
        loop._thread.join()
    _selector_loops.clear()


atexit.register(_atexit_callback)


class BaseAsyncIOLoop(IOLoop):
    def initialize(  # type: ignore
        self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any
    ) -> None:
        # asyncio_loop is always the real underlying IOLoop. This is used in
        # ioloop.py to maintain the asyncio-to-ioloop mappings.
        self.asyncio_loop = asyncio_loop
        # selector_loop is an event loop that implements the add_reader family of
        # methods. Usually the same as asyncio_loop but differs on platforms such
        # as windows where the default event loop does not implement these methods.
        self.selector_loop = asyncio_loop
        if hasattr(asyncio, "ProactorEventLoop") and isinstance(
            asyncio_loop, asyncio.ProactorEventLoop  # type: ignore
        ):
            # Ignore this line for mypy because the abstract method checker
            # doesn't understand dynamic proxies.
            self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop)  # type: ignore
        # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)
        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]
        # Set of fds listening for reads/writes
        self.readers = set()  # type: Set[int]
        self.writers = set()  # type: Set[int]
        self.closing = False
        # If an asyncio loop was closed through an asyncio interface
        # instead of IOLoop.close(), we'd never hear about it and may
        # have left a dangling reference in our map. In case an
        # application (or, more likely, a test suite) creates and
        # destroys a lot of event loops in this way, check here to
        # ensure that we don't have a lot of dead loops building up in
        # the map.
        #
        # TODO(bdarnell): consider making self.asyncio_loop a weakref
        # for AsyncIOMainLoop and make _ioloop_for_asyncio a
        # WeakKeyDictionary.
        for loop in list(IOLoop._ioloop_for_asyncio):
            if loop.is_closed():
                del IOLoop._ioloop_for_asyncio[loop]
        IOLoop._ioloop_for_asyncio[asyncio_loop] = self

        self._thread_identity = 0

        super().initialize(**kwargs)

        def assign_thread_identity() -> None:
            self._thread_identity = threading.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]
            self.remove_handler(fd)
            if all_fds:
                self.close_fd(fileobj)
        # Remove the mapping before closing the asyncio loop. If this
        # happened in the other order, we could race against another
        # initialize() call which would see the closed asyncio loop,
        # assume it was closed from the asyncio side, and do this
        # cleanup for us, leading to a KeyError.
        del IOLoop._ioloop_for_asyncio[self.asyncio_loop]
        if self.selector_loop is not self.asyncio_loop:
            self.selector_loop.close()
        self.asyncio_loop.close()

    def add_handler(
        self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int
    ) -> None:
        fd, fileobj = self.split_fd(fd)
        if fd in self.handlers:
            raise ValueError("fd %s added twice" % fd)
        self.handlers[fd] = (fileobj, handler)
        if events & IOLoop.READ:
            self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
            self.readers.add(fd)
        if events & IOLoop.WRITE:
            self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)
            self.writers.add(fd)

    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:
        fd, fileobj = self.split_fd(fd)
        if events & IOLoop.READ:
            if fd not in self.readers:
                self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
                self.readers.add(fd)
        else:
            if fd in self.readers:
                self.selector_loop.remove_reader(fd)
                self.readers.remove(fd)
        if events & IOLoop.WRITE:
            if fd not in self.writers:
                self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)
                self.writers.add(fd)
        else:
            if fd in self.writers:
                self.selector_loop.remove_writer(fd)
                self.writers.remove(fd)

    def remove_handler(self, fd: Union[int, _Selectable]) -> None:
        fd, fileobj = self.split_fd(fd)
        if fd not in self.handlers:
            return
        if fd in self.readers:
            self.selector_loop.remove_reader(fd)
            self.readers.remove(fd)
        if fd in self.writers:
            self.selector_loop.remove_writer(fd)
            self.writers.remove(fd)
        del self.handlers[fd]

    def _handle_events(self, fd: int, events: int) -> None:
        fileobj, handler_func = self.handlers[fd]
        handler_func(fileobj, events)

    def start(self) -> None:
        try:
            old_loop = asyncio.get_event_loop()
        except (RuntimeError, AssertionError):
            old_loop = None  # type: ignore
        try:
            self._setup_logging()
            asyncio.set_event_loop(self.asyncio_loop)
            self.asyncio_loop.run_forever()
        finally:
            asyncio.set_event_loop(old_loop)

    def stop(self) -> None:
        self.asyncio_loop.stop()

    def call_at(
        self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any
    ) -> object:
        # asyncio.call_at supports *args but not **kwargs, so bind them here.
        # We do not synchronize self.time and asyncio_loop.time, so
        # convert from absolute to relative.
        return self.asyncio_loop.call_later(
            max(0, when - self.time()),
            self._run_callback,
            functools.partial(callback, *args, **kwargs),
        )

    def remove_timeout(self, timeout: object) -> None:
        timeout.cancel()  # type: ignore

    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
        if threading.get_ident() == self._thread_identity:
            call_soon = self.asyncio_loop.call_soon
        else:
            call_soon = self.asyncio_loop.call_soon_threadsafe
        try:
            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
            # with the fact that we can't guarantee that an
            # add_callback that completes without error will
            # eventually execute).
            pass
        except AttributeError:
            # ProactorEventLoop may raise this instead of RuntimeError
            # if call_soon_threadsafe races with a call to close().
            # Swallow it too for consistency.
            pass

    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)
            )
        except RuntimeError:
            pass

    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:
        return self.asyncio_loop.set_default_executor(executor)


class AsyncIOMainLoop(BaseAsyncIOLoop):
    """``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the
    current ``asyncio`` event loop (i.e. the one returned by
    ``asyncio.get_event_loop()``).

    .. deprecated:: 5.0

       Now used automatically when appropriate; it is no longer necessary
       to refer to this class directly.

    .. versionchanged:: 5.0

       Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.
    """

    def initialize(self, **kwargs: Any) -> None:  # type: ignore
        super().initialize(asyncio.get_event_loop(), **kwargs)

    def make_current(self) -> None:
        # 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.
    This class follows the usual Tornado semantics for creating new
    ``IOLoops``; these loops are not necessarily related to the
    ``asyncio`` default event loop.

    Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object
    can be accessed with the ``asyncio_loop`` attribute.

    .. versionchanged:: 5.0

       When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets
       the current `asyncio` event loop.

    .. deprecated:: 5.0

       Now used automatically when appropriate; it is no longer necessary
       to refer to this class directly.
    """

    def initialize(self, **kwargs: Any) -> None:  # type: ignore
        self.is_current = False
        loop = asyncio.new_event_loop()
        try:
            super().initialize(loop, **kwargs)
        except Exception:
            # If initialize() does not succeed (taking ownership of the loop),
            # we have to close it.
            loop.close()
            raise

    def close(self, all_fds: bool = False) -> None:
        if self.is_current:
            self.clear_current()
        super().close(all_fds=all_fds)

    def make_current(self) -> None:
        if not self.is_current:
            try:
                self.old_asyncio = asyncio.get_event_loop()
            except (RuntimeError, AssertionError):
                self.old_asyncio = None  # type: ignore
            self.is_current = True
        asyncio.set_event_loop(self.asyncio_loop)

    def _clear_current_hook(self) -> None:
        if self.is_current:
            asyncio.set_event_loop(self.old_asyncio)
            self.is_current = False


def to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:
    """Convert an `asyncio.Future` to a `tornado.concurrent.Future`.

    .. versionadded:: 4.1

    .. deprecated:: 5.0
       Tornado ``Futures`` have been merged with `asyncio.Future`,
       so this method is now a no-op.
    """
    return asyncio_future


def to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:
    """Convert a Tornado yieldable object to an `asyncio.Future`.

    .. versionadded:: 4.1

    .. versionchanged:: 4.3
       Now accepts any yieldable object, not just
       `tornado.concurrent.Future`.

    .. deprecated:: 5.0
       Tornado ``Futures`` have been merged with `asyncio.Future`,
       so this method is now equivalent to `tornado.gen.convert_yielded`.
    """
    return convert_yielded(tornado_future)


if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
    # "Any thread" and "selector" should be orthogonal, but there's not a clean
    # interface for composing policies so pick the right base.
    _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy  # type: ignore
else:
    _BasePolicy = asyncio.DefaultEventLoopPolicy


class AnyThreadEventLoopPolicy(_BasePolicy):  # type: ignore
    """Event loop policy that allows loop creation on any thread.

    The default `asyncio` event loop policy only automatically creates
    event loops in the main threads. Other threads must create event
    loops explicitly or `asyncio.get_event_loop` (and therefore
    `.IOLoop.current`) will fail. Installing this policy allows event
    loops to be created automatically on any thread, matching the
    behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).

    Usage::

        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())

    .. versionadded:: 5.0

    """

    def get_event_loop(self) -> asyncio.AbstractEventLoop:
        try:
            return super().get_event_loop()
        except (RuntimeError, AssertionError):
            # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie)
            # and changed to a RuntimeError in 3.4.3.
            # "There is no current event loop in thread %r"
            loop = self.new_event_loop()
            self.set_event_loop(loop)
            return loop


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.

    Typically used via ``AddThreadSelectorEventLoop``,
    but can be attached to a running asyncio loop.
    """

    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:
        self._real_loop = real_loop

        # Create a thread to run the select system call. We manage this thread
        # manually so we can trigger a clean shutdown from an atexit hook. Note
        # that due to the order of operations at shutdown, only daemon threads
        # can be shut down in this way (non-daemon threads would require the
        # introduction of a new hook: https://bugs.python.org/issue41962)
        self._select_cond = threading.Condition()
        self._select_args = (
            None
        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]
        self._closing_selector = False
        self._thread = threading.Thread(
            name="Tornado selector",
            daemon=True,
            target=self._run_select,
        )
        self._thread.start()
        # Start the select loop once the loop is started.
        self._real_loop.call_soon(self._start_select)

        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]
        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]

        # Writing to _waker_w will wake up the selector thread, which
        # watches for _waker_r to be readable.
        self._waker_r, self._waker_w = socket.socketpair()
        self._waker_r.setblocking(False)
        self._waker_w.setblocking(False)
        _selector_loops.add(self)
        self.add_reader(self._waker_r, self._consume_waker)

    def __del__(self) -> None:
        # If the top-level application code uses asyncio interfaces to
        # start and stop the event loop, no objects created in Tornado
        # can get a clean shutdown notification. If we're just left to
        # be GC'd, we must explicitly close our sockets to avoid
        # logging warnings.
        _selector_loops.discard(self)
        self._waker_r.close()
        self._waker_w.close()

    def close(self) -> None:
        with self._select_cond:
            self._closing_selector = True
            self._select_cond.notify()
        self._wake_selector()
        self._thread.join()
        _selector_loops.discard(self)
        self._waker_r.close()
        self._waker_w.close()

    def _wake_selector(self) -> None:
        try:
            self._waker_w.send(b"a")
        except BlockingIOError:
            pass

    def _consume_waker(self) -> None:
        try:
            self._waker_r.recv(1024)
        except BlockingIOError:
            pass

    def _start_select(self) -> None:
        # Capture reader and writer sets here in the event loop
        # thread to avoid any problems with concurrent
        # modification while the select loop uses them.
        with self._select_cond:
            assert self._select_args is None
            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))
            self._select_cond.notify()

    def _run_select(self) -> None:
        while True:
            with self._select_cond:
                while self._select_args is None and not self._closing_selector:
                    self._select_cond.wait()
                if self._closing_selector:
                    return
                assert self._select_args is not None
                to_read, to_write = self._select_args
                self._select_args = None

            # We use the simpler interface of the select module instead of
            # the more stateful interface in the selectors module because
            # this class is only intended for use on windows, where
            # select.select is the only option. The selector interface
            # does not have well-documented thread-safety semantics that
            # we can rely on so ensuring proper synchronization would be
            # tricky.
            try:
                # On windows, selecting on a socket for write will not
                # return the socket when there is an error (but selecting
                # for reads works). Also select for errors when selecting
                # for writes, and merge the results.
                #
                # This pattern is also used in
                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317
                rs, ws, xs = select.select(to_read, to_write, to_write)
                ws = ws + xs
            except OSError as e:
                # After remove_reader or remove_writer is called, the file
                # descriptor may subsequently be closed on the event loop
                # thread. It's possible that this select thread hasn't
                # gotten into the select system call by the time that
                # happens in which case (at least on macOS), select may
                # raise a "bad file descriptor" error. If we get that
                # error, check and see if we're also being woken up by
                # polling the waker alone. If we are, just return to the
                # event loop and we'll get the updated set of file
                # descriptors on the next iteration. Otherwise, raise the
                # original error.
                if e.errno == getattr(errno, "WSAENOTSOCK", errno.EBADF):
                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)
                    if rs:
                        ws = []
                    else:
                        raise
                else:
                    raise
            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)

    def _handle_select(
        self, rs: List["_FileDescriptorLike"], ws: List["_FileDescriptorLike"]
    ) -> None:
        for r in rs:
            self._handle_event(r, self._readers)
        for w in ws:
            self._handle_event(w, self._writers)
        self._start_select()

    def _handle_event(
        self,
        fd: "_FileDescriptorLike",
        cb_map: Dict["_FileDescriptorLike", Callable],
    ) -> None:
        try:
            callback = cb_map[fd]
        except KeyError:
            return
        callback()

    def add_reader(
        self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any
    ) -> None:
        self._readers[fd] = functools.partial(callback, *args)
        self._wake_selector()

    def add_writer(
        self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any
    ) -> None:
        self._writers[fd] = functools.partial(callback, *args)
        self._wake_selector()

    def remove_reader(self, fd: "_FileDescriptorLike") -> None:
        del self._readers[fd]
        self._wake_selector()

    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)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Calling `close()` Multiple Times on `AddThreadSelectorEventLoop` Raises OSError

**Description:**
When the `close()` method is invoked more than once on an instance of `AddThreadSelectorEventLoop`, the second call fails with an `OSError`. This behavior prevents the method from being idempotent, which is unexpected and can lead to runtime errors in applications that attempt to close the event loop multiple times.

**Example Code:**
```python
loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())
loop.close()
loop.close()
```

**Expected Behavior:**
Calling `close()` multiple times on the same `AddThreadSelectorEventLoop` instance should have no adverse effects after the first call. Subsequent calls should be safely ignored without raising any exceptions.

**Actual Behavior:**
The second call to `close()` raises an `OSError` with the message `[Errno 9] Bad file descriptor`, causing the application to crash unexpectedly.

[/ISSUE]
{
    "AnyThreadEventLoopPolicyTest.test_asyncio_accessor": "PASSED",
    "AnyThreadEventLoopPolicyTest.test_tornado_accessor": "PASSED",
    "AsyncIOLoopTest.test_add_thread_close_idempotent": "PASSED",
    "AsyncIOLoopTest.test_asyncio_adapter": "PASSED",
    "AsyncIOLoopTest.test_asyncio_callback": "PASSED",
    "AsyncIOLoopTest.test_asyncio_future": "PASSED",
    "AsyncIOLoopTest.test_asyncio_yield_from": "PASSED",
    "LeakTest.test_asyncio_close_leak": "PASSED",
    "LeakTest.test_ioloop_close_leak": "PASSED"
}
diff --git a/tornado/platform/asyncio.py b/tornado/platform/asyncio.py
index b07af3a9..3863e78f 100644
--- a/tornado/platform/asyncio.py
+++ b/tornado/platform/asyncio.py
@@ -412,6 +412,8 @@ class SelectorThread:
     but can be attached to a running asyncio loop.
     """
 
+    _closed = False
+
     def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:
         self._real_loop = real_loop
 
@@ -456,6 +458,8 @@ class SelectorThread:
         self._waker_w.close()
 
     def close(self) -> None:
+        if self._closed:
+            return
         with self._select_cond:
             self._closing_selector = True
             self._select_cond.notify()
@@ -464,6 +468,7 @@ class SelectorThread:
         _selector_loops.discard(self)
         self._waker_r.close()
         self._waker_w.close()
+        self._closed = True
 
     def _wake_selector(self) -> None:
         try:

{"tornado/platform/asyncio.py": "\"\"\"Bridges between the `asyncio` module and Tornado IOLoop.\n\n.. versionadded:: 3.2\n\nThis module integrates Tornado with the ``asyncio`` module introduced\nin Python 3.4. This makes it possible to combine the two libraries on\nthe same event loop.\n\n.. deprecated:: 5.0\n\n   While the code in this module is still used, it is now enabled\n   automatically when `asyncio` is available, so applications should\n   no longer need to refer to this module directly.\n\n.. note::\n\n   Tornado is designed to use a selector-based event loop. On Windows,\n   where a proactor-based event loop has been the default since Python 3.8,\n   a selector event loop is emulated by running ``select`` on a separate thread.\n   Configuring ``asyncio`` to use a selector event loop may improve performance\n   of Tornado (but may reduce performance of other ``asyncio``-based libraries\n   in the same process).\n\"\"\"\n\nimport asyncio\nimport atexit\nimport concurrent.futures\nimport errno\nimport functools\nimport select\nimport socket\nimport sys\nimport threading\nimport typing\nfrom tornado.gen import convert_yielded\nfrom tornado.ioloop import IOLoop, _Selectable\n\nfrom typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict\n\nif typing.TYPE_CHECKING:\n    from typing import Set  # noqa: F401\n    from typing_extensions import Protocol\n\n    class _HasFileno(Protocol):\n        def fileno(self) -> int:\n            pass\n\n    _FileDescriptorLike = Union[int, _HasFileno]\n\n_T = TypeVar(\"_T\")\n\n\n# Collection of selector thread event loops to shut down on exit.\n_selector_loops = set()  # type: Set[SelectorThread]\n\n\ndef _atexit_callback() -> None:\n    for loop in _selector_loops:\n        with loop._select_cond:\n            loop._closing_selector = True\n            loop._select_cond.notify()\n        try:\n            loop._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n        # If we don't join our (daemon) thread here, we may get a deadlock\n        # during interpreter shutdown. I don't really understand why. This\n        # deadlock happens every time in CI (both travis and appveyor) but\n        # I've never been able to reproduce locally.\n        loop._thread.join()\n    _selector_loops.clear()\n\n\natexit.register(_atexit_callback)\n\n\nclass BaseAsyncIOLoop(IOLoop):\n    def initialize(  # type: ignore\n        self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any\n    ) -> None:\n        # asyncio_loop is always the real underlying IOLoop. This is used in\n        # ioloop.py to maintain the asyncio-to-ioloop mappings.\n        self.asyncio_loop = asyncio_loop\n        # selector_loop is an event loop that implements the add_reader family of\n        # methods. Usually the same as asyncio_loop but differs on platforms such\n        # as windows where the default event loop does not implement these methods.\n        self.selector_loop = asyncio_loop\n        if hasattr(asyncio, \"ProactorEventLoop\") and isinstance(\n            asyncio_loop, asyncio.ProactorEventLoop  # type: ignore\n        ):\n            # Ignore this line for mypy because the abstract method checker\n            # doesn't understand dynamic proxies.\n            self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop)  # type: ignore\n        # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)\n        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]\n        # Set of fds listening for reads/writes\n        self.readers = set()  # type: Set[int]\n        self.writers = set()  # type: Set[int]\n        self.closing = False\n        # If an asyncio loop was closed through an asyncio interface\n        # instead of IOLoop.close(), we'd never hear about it and may\n        # have left a dangling reference in our map. In case an\n        # application (or, more likely, a test suite) creates and\n        # destroys a lot of event loops in this way, check here to\n        # ensure that we don't have a lot of dead loops building up in\n        # the map.\n        #\n        # TODO(bdarnell): consider making self.asyncio_loop a weakref\n        # for AsyncIOMainLoop and make _ioloop_for_asyncio a\n        # WeakKeyDictionary.\n        for loop in list(IOLoop._ioloop_for_asyncio):\n            if loop.is_closed():\n                del IOLoop._ioloop_for_asyncio[loop]\n        IOLoop._ioloop_for_asyncio[asyncio_loop] = self\n\n        self._thread_identity = 0\n\n        super().initialize(**kwargs)\n\n        def assign_thread_identity() -> None:\n            self._thread_identity = threading.get_ident()\n\n        self.add_callback(assign_thread_identity)\n\n    def close(self, all_fds: bool = False) -> None:\n        self.closing = True\n        for fd in list(self.handlers):\n            fileobj, handler_func = self.handlers[fd]\n            self.remove_handler(fd)\n            if all_fds:\n                self.close_fd(fileobj)\n        # Remove the mapping before closing the asyncio loop. If this\n        # happened in the other order, we could race against another\n        # initialize() call which would see the closed asyncio loop,\n        # assume it was closed from the asyncio side, and do this\n        # cleanup for us, leading to a KeyError.\n        del IOLoop._ioloop_for_asyncio[self.asyncio_loop]\n        if self.selector_loop is not self.asyncio_loop:\n            self.selector_loop.close()\n        self.asyncio_loop.close()\n\n    def add_handler(\n        self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int\n    ) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd in self.handlers:\n            raise ValueError(\"fd %s added twice\" % fd)\n        self.handlers[fd] = (fileobj, handler)\n        if events & IOLoop.READ:\n            self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n            self.readers.add(fd)\n        if events & IOLoop.WRITE:\n            self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n            self.writers.add(fd)\n\n    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if events & IOLoop.READ:\n            if fd not in self.readers:\n                self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)\n                self.readers.add(fd)\n        else:\n            if fd in self.readers:\n                self.selector_loop.remove_reader(fd)\n                self.readers.remove(fd)\n        if events & IOLoop.WRITE:\n            if fd not in self.writers:\n                self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)\n                self.writers.add(fd)\n        else:\n            if fd in self.writers:\n                self.selector_loop.remove_writer(fd)\n                self.writers.remove(fd)\n\n    def remove_handler(self, fd: Union[int, _Selectable]) -> None:\n        fd, fileobj = self.split_fd(fd)\n        if fd not in self.handlers:\n            return\n        if fd in self.readers:\n            self.selector_loop.remove_reader(fd)\n            self.readers.remove(fd)\n        if fd in self.writers:\n            self.selector_loop.remove_writer(fd)\n            self.writers.remove(fd)\n        del self.handlers[fd]\n\n    def _handle_events(self, fd: int, events: int) -> None:\n        fileobj, handler_func = self.handlers[fd]\n        handler_func(fileobj, events)\n\n    def start(self) -> None:\n        try:\n            old_loop = asyncio.get_event_loop()\n        except (RuntimeError, AssertionError):\n            old_loop = None  # type: ignore\n        try:\n            self._setup_logging()\n            asyncio.set_event_loop(self.asyncio_loop)\n            self.asyncio_loop.run_forever()\n        finally:\n            asyncio.set_event_loop(old_loop)\n\n    def stop(self) -> None:\n        self.asyncio_loop.stop()\n\n    def call_at(\n        self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any\n    ) -> object:\n        # asyncio.call_at supports *args but not **kwargs, so bind them here.\n        # We do not synchronize self.time and asyncio_loop.time, so\n        # convert from absolute to relative.\n        return self.asyncio_loop.call_later(\n            max(0, when - self.time()),\n            self._run_callback,\n            functools.partial(callback, *args, **kwargs),\n        )\n\n    def remove_timeout(self, timeout: object) -> None:\n        timeout.cancel()  # type: ignore\n\n    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:\n        if threading.get_ident() == self._thread_identity:\n            call_soon = self.asyncio_loop.call_soon\n        else:\n            call_soon = self.asyncio_loop.call_soon_threadsafe\n        try:\n            call_soon(self._run_callback, functools.partial(callback, *args, **kwargs))\n        except RuntimeError:\n            # \"Event loop is closed\". Swallow the exception for\n            # consistency with PollIOLoop (and logical consistency\n            # with the fact that we can't guarantee that an\n            # add_callback that completes without error will\n            # eventually execute).\n            pass\n        except AttributeError:\n            # ProactorEventLoop may raise this instead of RuntimeError\n            # if call_soon_threadsafe races with a call to close().\n            # Swallow it too for consistency.\n            pass\n\n    def add_callback_from_signal(\n        self, callback: Callable, *args: Any, **kwargs: Any\n    ) -> None:\n        try:\n            self.asyncio_loop.call_soon_threadsafe(\n                self._run_callback, functools.partial(callback, *args, **kwargs)\n            )\n        except RuntimeError:\n            pass\n\n    def run_in_executor(\n        self,\n        executor: Optional[concurrent.futures.Executor],\n        func: Callable[..., _T],\n        *args: Any\n    ) -> Awaitable[_T]:\n        return self.asyncio_loop.run_in_executor(executor, func, *args)\n\n    def set_default_executor(self, executor: concurrent.futures.Executor) -> None:\n        return self.asyncio_loop.set_default_executor(executor)\n\n\nclass AsyncIOMainLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the\n    current ``asyncio`` event loop (i.e. the one returned by\n    ``asyncio.get_event_loop()``).\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n\n    .. versionchanged:: 5.0\n\n       Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        super().initialize(asyncio.get_event_loop(), **kwargs)\n\n    def make_current(self) -> None:\n        # AsyncIOMainLoop already refers to the current asyncio loop so\n        # nothing to do here.\n        pass\n\n\nclass AsyncIOLoop(BaseAsyncIOLoop):\n    \"\"\"``AsyncIOLoop`` is an `.IOLoop` that runs on an ``asyncio`` event loop.\n    This class follows the usual Tornado semantics for creating new\n    ``IOLoops``; these loops are not necessarily related to the\n    ``asyncio`` default event loop.\n\n    Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object\n    can be accessed with the ``asyncio_loop`` attribute.\n\n    .. versionchanged:: 5.0\n\n       When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets\n       the current `asyncio` event loop.\n\n    .. deprecated:: 5.0\n\n       Now used automatically when appropriate; it is no longer necessary\n       to refer to this class directly.\n    \"\"\"\n\n    def initialize(self, **kwargs: Any) -> None:  # type: ignore\n        self.is_current = False\n        loop = asyncio.new_event_loop()\n        try:\n            super().initialize(loop, **kwargs)\n        except Exception:\n            # If initialize() does not succeed (taking ownership of the loop),\n            # we have to close it.\n            loop.close()\n            raise\n\n    def close(self, all_fds: bool = False) -> None:\n        if self.is_current:\n            self.clear_current()\n        super().close(all_fds=all_fds)\n\n    def make_current(self) -> None:\n        if not self.is_current:\n            try:\n                self.old_asyncio = asyncio.get_event_loop()\n            except (RuntimeError, AssertionError):\n                self.old_asyncio = None  # type: ignore\n            self.is_current = True\n        asyncio.set_event_loop(self.asyncio_loop)\n\n    def _clear_current_hook(self) -> None:\n        if self.is_current:\n            asyncio.set_event_loop(self.old_asyncio)\n            self.is_current = False\n\n\ndef to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert an `asyncio.Future` to a `tornado.concurrent.Future`.\n\n    .. versionadded:: 4.1\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now a no-op.\n    \"\"\"\n    return asyncio_future\n\n\ndef to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:\n    \"\"\"Convert a Tornado yieldable object to an `asyncio.Future`.\n\n    .. versionadded:: 4.1\n\n    .. versionchanged:: 4.3\n       Now accepts any yieldable object, not just\n       `tornado.concurrent.Future`.\n\n    .. deprecated:: 5.0\n       Tornado ``Futures`` have been merged with `asyncio.Future`,\n       so this method is now equivalent to `tornado.gen.convert_yielded`.\n    \"\"\"\n    return convert_yielded(tornado_future)\n\n\nif sys.platform == \"win32\" and hasattr(asyncio, \"WindowsSelectorEventLoopPolicy\"):\n    # \"Any thread\" and \"selector\" should be orthogonal, but there's not a clean\n    # interface for composing policies so pick the right base.\n    _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy  # type: ignore\nelse:\n    _BasePolicy = asyncio.DefaultEventLoopPolicy\n\n\nclass AnyThreadEventLoopPolicy(_BasePolicy):  # type: ignore\n    \"\"\"Event loop policy that allows loop creation on any thread.\n\n    The default `asyncio` event loop policy only automatically creates\n    event loops in the main threads. Other threads must create event\n    loops explicitly or `asyncio.get_event_loop` (and therefore\n    `.IOLoop.current`) will fail. Installing this policy allows event\n    loops to be created automatically on any thread, matching the\n    behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).\n\n    Usage::\n\n        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())\n\n    .. versionadded:: 5.0\n\n    \"\"\"\n\n    def get_event_loop(self) -> asyncio.AbstractEventLoop:\n        try:\n            return super().get_event_loop()\n        except (RuntimeError, AssertionError):\n            # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie)\n            # and changed to a RuntimeError in 3.4.3.\n            # \"There is no current event loop in thread %r\"\n            loop = self.new_event_loop()\n            self.set_event_loop(loop)\n            return loop\n\n\nclass SelectorThread:\n    \"\"\"Define ``add_reader`` methods to be called in a background select thread.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user;\n    all callbacks are run on the wrapped event loop's thread.\n\n    Typically used via ``AddThreadSelectorEventLoop``,\n    but can be attached to a running asyncio loop.\n    \"\"\"\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n\n        # Create a thread to run the select system call. We manage this thread\n        # manually so we can trigger a clean shutdown from an atexit hook. Note\n        # that due to the order of operations at shutdown, only daemon threads\n        # can be shut down in this way (non-daemon threads would require the\n        # introduction of a new hook: https://bugs.python.org/issue41962)\n        self._select_cond = threading.Condition()\n        self._select_args = (\n            None\n        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]\n        self._closing_selector = False\n        self._thread = threading.Thread(\n            name=\"Tornado selector\",\n            daemon=True,\n            target=self._run_select,\n        )\n        self._thread.start()\n        # Start the select loop once the loop is started.\n        self._real_loop.call_soon(self._start_select)\n\n        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]\n        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]\n\n        # Writing to _waker_w will wake up the selector thread, which\n        # watches for _waker_r to be readable.\n        self._waker_r, self._waker_w = socket.socketpair()\n        self._waker_r.setblocking(False)\n        self._waker_w.setblocking(False)\n        _selector_loops.add(self)\n        self.add_reader(self._waker_r, self._consume_waker)\n\n    def __del__(self) -> None:\n        # If the top-level application code uses asyncio interfaces to\n        # start and stop the event loop, no objects created in Tornado\n        # can get a clean shutdown notification. If we're just left to\n        # be GC'd, we must explicitly close our sockets to avoid\n        # logging warnings.\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def close(self) -> None:\n        with self._select_cond:\n            self._closing_selector = True\n            self._select_cond.notify()\n        self._wake_selector()\n        self._thread.join()\n        _selector_loops.discard(self)\n        self._waker_r.close()\n        self._waker_w.close()\n\n    def _wake_selector(self) -> None:\n        try:\n            self._waker_w.send(b\"a\")\n        except BlockingIOError:\n            pass\n\n    def _consume_waker(self) -> None:\n        try:\n            self._waker_r.recv(1024)\n        except BlockingIOError:\n            pass\n\n    def _start_select(self) -> None:\n        # Capture reader and writer sets here in the event loop\n        # thread to avoid any problems with concurrent\n        # modification while the select loop uses them.\n        with self._select_cond:\n            assert self._select_args is None\n            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))\n            self._select_cond.notify()\n\n    def _run_select(self) -> None:\n        while True:\n            with self._select_cond:\n                while self._select_args is None and not self._closing_selector:\n                    self._select_cond.wait()\n                if self._closing_selector:\n                    return\n                assert self._select_args is not None\n                to_read, to_write = self._select_args\n                self._select_args = None\n\n            # We use the simpler interface of the select module instead of\n            # the more stateful interface in the selectors module because\n            # this class is only intended for use on windows, where\n            # select.select is the only option. The selector interface\n            # does not have well-documented thread-safety semantics that\n            # we can rely on so ensuring proper synchronization would be\n            # tricky.\n            try:\n                # On windows, selecting on a socket for write will not\n                # return the socket when there is an error (but selecting\n                # for reads works). Also select for errors when selecting\n                # for writes, and merge the results.\n                #\n                # This pattern is also used in\n                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317\n                rs, ws, xs = select.select(to_read, to_write, to_write)\n                ws = ws + xs\n            except OSError as e:\n                # After remove_reader or remove_writer is called, the file\n                # descriptor may subsequently be closed on the event loop\n                # thread. It's possible that this select thread hasn't\n                # gotten into the select system call by the time that\n                # happens in which case (at least on macOS), select may\n                # raise a \"bad file descriptor\" error. If we get that\n                # error, check and see if we're also being woken up by\n                # polling the waker alone. If we are, just return to the\n                # event loop and we'll get the updated set of file\n                # descriptors on the next iteration. Otherwise, raise the\n                # original error.\n                if e.errno == getattr(errno, \"WSAENOTSOCK\", errno.EBADF):\n                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)\n                    if rs:\n                        ws = []\n                    else:\n                        raise\n                else:\n                    raise\n            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)\n\n    def _handle_select(\n        self, rs: List[\"_FileDescriptorLike\"], ws: List[\"_FileDescriptorLike\"]\n    ) -> None:\n        for r in rs:\n            self._handle_event(r, self._readers)\n        for w in ws:\n            self._handle_event(w, self._writers)\n        self._start_select()\n\n    def _handle_event(\n        self,\n        fd: \"_FileDescriptorLike\",\n        cb_map: Dict[\"_FileDescriptorLike\", Callable],\n    ) -> None:\n        try:\n            callback = cb_map[fd]\n        except KeyError:\n            return\n        callback()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._readers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        self._writers[fd] = functools.partial(callback, *args)\n        self._wake_selector()\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._readers[fd]\n        self._wake_selector()\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        del self._writers[fd]\n        self._wake_selector()\n\n\nclass AddThreadSelectorEventLoop(asyncio.AbstractEventLoop):\n    \"\"\"Wrap an event loop to add implementations of the ``add_reader`` method family.\n\n    Instances of this class start a second thread to run a selector.\n    This thread is completely hidden from the user; all callbacks are\n    run on the wrapped event loop's thread.\n\n    This class is used automatically by Tornado; applications should not need\n    to refer to it directly.\n\n    It is safe to wrap any event loop with this class, although it only makes sense\n    for event loops that do not implement the ``add_reader`` family of methods\n    themselves (i.e. ``WindowsProactorEventLoop``)\n\n    Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop.\n\n    \"\"\"\n\n    # This class is a __getattribute__-based proxy. All attributes other than those\n    # in this set are proxied through to the underlying loop.\n    MY_ATTRIBUTES = {\n        \"_real_loop\",\n        \"_selector\",\n        \"add_reader\",\n        \"add_writer\",\n        \"close\",\n        \"remove_reader\",\n        \"remove_writer\",\n    }\n\n    def __getattribute__(self, name: str) -> Any:\n        if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES:\n            return super().__getattribute__(name)\n        return getattr(self._real_loop, name)\n\n    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:\n        self._real_loop = real_loop\n        self._selector = SelectorThread(real_loop)\n\n    def close(self) -> None:\n        self._selector.close()\n        self._real_loop.close()\n\n    def add_reader(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_reader(fd, callback, *args)\n\n    def add_writer(\n        self, fd: \"_FileDescriptorLike\", callback: Callable[..., None], *args: Any\n    ) -> None:\n        return self._selector.add_writer(fd, callback, *args)\n\n    def remove_reader(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_reader(fd)\n\n    def remove_writer(self, fd: \"_FileDescriptorLike\") -> None:\n        return self._selector.remove_writer(fd)\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Calling `close()` Multiple Times on `AddThreadSelectorEventLoop` Raises OSError

**Description:**
When the `close()` method is invoked more than once on an instance of `AddThreadSelectorEventLoop`, the second call fails with an `OSError`. This behavior prevents the method from being idempotent, which is unexpected and can lead to runtime errors in applications that attempt to close the event loop multiple times.

**Example Code:**
```python
loop = AddThreadSelectorEventLoop(asyncio.get_event_loop())
loop.close()
loop.close()
```

**Expected Behavior:**
Calling `close()` multiple times on the same `AddThreadSelectorEventLoop` instance should have no adverse effects after the first call. Subsequent calls should be safely ignored without raising any exceptions.

**Actual Behavior:**
The second call to `close()` raises an `OSError` with the message `[Errno 9] Bad file descriptor`, causing the application to crash unexpectedly.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### tornado/platform/asyncio.py
"""Bridges between the `asyncio` module and Tornado IOLoop.

.. versionadded:: 3.2

This module integrates Tornado with the ``asyncio`` module introduced
in Python 3.4. This makes it possible to combine the two libraries on
the same event loop.

.. deprecated:: 5.0

   While the code in this module is still used, it is now enabled
   automatically when `asyncio` is available, so applications should
   no longer need to refer to this module directly.

.. note::

   Tornado is designed to use a selector-based event loop. On Windows,
   where a proactor-based event loop has been the default since Python 3.8,
   a selector event loop is emulated by running ``select`` on a separate thread.
   Configuring ``asyncio`` to use a selector event loop may improve performance
   of Tornado (but may reduce performance of other ``asyncio``-based libraries
   in the same process).
"""

import asyncio
import atexit
import concurrent.futures
import errno
import functools
import select
import socket
import sys
import threading
import typing
from tornado.gen import convert_yielded
from tornado.ioloop import IOLoop, _Selectable

from typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict

if typing.TYPE_CHECKING:
    from typing import Set  # noqa: F401
    from typing_extensions import Protocol

    class _HasFileno(Protocol):
        def fileno(self) -> int:
            pass

    _FileDescriptorLike = Union[int, _HasFileno]

_T = TypeVar("_T")


# Collection of selector thread event loops to shut down on exit.
_selector_loops = set()  # type: Set[SelectorThread]


def _atexit_callback() -> None:
    for loop in _selector_loops:
        with loop._select_cond:
            loop._closing_selector = True
            loop._select_cond.notify()
        try:
            loop._waker_w.send(b"a")
        except BlockingIOError:
            pass
        # If we don't join our (daemon) thread here, we may get a deadlock
        # during interpreter shutdown. I don't really understand why. This
        # deadlock happens every time in CI (both travis and appveyor) but
        # I've never been able to reproduce locally.
        loop._thread.join()
    _selector_loops.clear()


atexit.register(_atexit_callback)


class BaseAsyncIOLoop(IOLoop):
    def initialize(  # type: ignore
        self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any
    ) -> None:
        # asyncio_loop is always the real underlying IOLoop. This is used in
        # ioloop.py to maintain the asyncio-to-ioloop mappings.
        self.asyncio_loop = asyncio_loop
        # selector_loop is an event loop that implements the add_reader family of
        # methods. Usually the same as asyncio_loop but differs on platforms such
        # as windows where the default event loop does not implement these methods.
        self.selector_loop = asyncio_loop
        if hasattr(asyncio, "ProactorEventLoop") and isinstance(
            asyncio_loop, asyncio.ProactorEventLoop  # type: ignore
        ):
            # Ignore this line for mypy because the abstract method checker
            # doesn't understand dynamic proxies.
            self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop)  # type: ignore
        # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler)
        self.handlers = {}  # type: Dict[int, Tuple[Union[int, _Selectable], Callable]]
        # Set of fds listening for reads/writes
        self.readers = set()  # type: Set[int]
        self.writers = set()  # type: Set[int]
        self.closing = False
        # If an asyncio loop was closed through an asyncio interface
        # instead of IOLoop.close(), we'd never hear about it and may
        # have left a dangling reference in our map. In case an
        # application (or, more likely, a test suite) creates and
        # destroys a lot of event loops in this way, check here to
        # ensure that we don't have a lot of dead loops building up in
        # the map.
        #
        # TODO(bdarnell): consider making self.asyncio_loop a weakref
        # for AsyncIOMainLoop and make _ioloop_for_asyncio a
        # WeakKeyDictionary.
        for loop in list(IOLoop._ioloop_for_asyncio):
            if loop.is_closed():
                del IOLoop._ioloop_for_asyncio[loop]
        IOLoop._ioloop_for_asyncio[asyncio_loop] = self

        self._thread_identity = 0

        super().initialize(**kwargs)

        def assign_thread_identity() -> None:
            self._thread_identity = threading.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]
            self.remove_handler(fd)
            if all_fds:
                self.close_fd(fileobj)
        # Remove the mapping before closing the asyncio loop. If this
        # happened in the other order, we could race against another
        # initialize() call which would see the closed asyncio loop,
        # assume it was closed from the asyncio side, and do this
        # cleanup for us, leading to a KeyError.
        del IOLoop._ioloop_for_asyncio[self.asyncio_loop]
        if self.selector_loop is not self.asyncio_loop:
            self.selector_loop.close()
        self.asyncio_loop.close()

    def add_handler(
        self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int
    ) -> None:
        fd, fileobj = self.split_fd(fd)
        if fd in self.handlers:
            raise ValueError("fd %s added twice" % fd)
        self.handlers[fd] = (fileobj, handler)
        if events & IOLoop.READ:
            self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
            self.readers.add(fd)
        if events & IOLoop.WRITE:
            self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)
            self.writers.add(fd)

    def update_handler(self, fd: Union[int, _Selectable], events: int) -> None:
        fd, fileobj = self.split_fd(fd)
        if events & IOLoop.READ:
            if fd not in self.readers:
                self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ)
                self.readers.add(fd)
        else:
            if fd in self.readers:
                self.selector_loop.remove_reader(fd)
                self.readers.remove(fd)
        if events & IOLoop.WRITE:
            if fd not in self.writers:
                self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE)
                self.writers.add(fd)
        else:
            if fd in self.writers:
                self.selector_loop.remove_writer(fd)
                self.writers.remove(fd)

    def remove_handler(self, fd: Union[int, _Selectable]) -> None:
        fd, fileobj = self.split_fd(fd)
        if fd not in self.handlers:
            return
        if fd in self.readers:
            self.selector_loop.remove_reader(fd)
            self.readers.remove(fd)
        if fd in self.writers:
            self.selector_loop.remove_writer(fd)
            self.writers.remove(fd)
        del self.handlers[fd]

    def _handle_events(self, fd: int, events: int) -> None:
        fileobj, handler_func = self.handlers[fd]
        handler_func(fileobj, events)

    def start(self) -> None:
        try:
            old_loop = asyncio.get_event_loop()
        except (RuntimeError, AssertionError):
            old_loop = None  # type: ignore
        try:
            self._setup_logging()
            asyncio.set_event_loop(self.asyncio_loop)
            self.asyncio_loop.run_forever()
        finally:
            asyncio.set_event_loop(old_loop)

    def stop(self) -> None:
        self.asyncio_loop.stop()

    def call_at(
        self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any
    ) -> object:
        # asyncio.call_at supports *args but not **kwargs, so bind them here.
        # We do not synchronize self.time and asyncio_loop.time, so
        # convert from absolute to relative.
        return self.asyncio_loop.call_later(
            max(0, when - self.time()),
            self._run_callback,
            functools.partial(callback, *args, **kwargs),
        )

    def remove_timeout(self, timeout: object) -> None:
        timeout.cancel()  # type: ignore

    def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None:
        if threading.get_ident() == self._thread_identity:
            call_soon = self.asyncio_loop.call_soon
        else:
            call_soon = self.asyncio_loop.call_soon_threadsafe
        try:
            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
            # with the fact that we can't guarantee that an
            # add_callback that completes without error will
            # eventually execute).
            pass
        except AttributeError:
            # ProactorEventLoop may raise this instead of RuntimeError
            # if call_soon_threadsafe races with a call to close().
            # Swallow it too for consistency.
            pass

    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)
            )
        except RuntimeError:
            pass

    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:
        return self.asyncio_loop.set_default_executor(executor)


class AsyncIOMainLoop(BaseAsyncIOLoop):
    """``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the
    current ``asyncio`` event loop (i.e. the one returned by
    ``asyncio.get_event_loop()``).

    .. deprecated:: 5.0

       Now used automatically when appropriate; it is no longer necessary
       to refer to this class directly.

    .. versionchanged:: 5.0

       Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop.
    """

    def initialize(self, **kwargs: Any) -> None:  # type: ignore
        super().initialize(asyncio.get_event_loop(), **kwargs)

    def make_current(self) -> None:
        # 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.
    This class follows the usual Tornado semantics for creating new
    ``IOLoops``; these loops are not necessarily related to the
    ``asyncio`` default event loop.

    Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object
    can be accessed with the ``asyncio_loop`` attribute.

    .. versionchanged:: 5.0

       When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets
       the current `asyncio` event loop.

    .. deprecated:: 5.0

       Now used automatically when appropriate; it is no longer necessary
       to refer to this class directly.
    """

    def initialize(self, **kwargs: Any) -> None:  # type: ignore
        self.is_current = False
        loop = asyncio.new_event_loop()
        try:
            super().initialize(loop, **kwargs)
        except Exception:
            # If initialize() does not succeed (taking ownership of the loop),
            # we have to close it.
            loop.close()
            raise

    def close(self, all_fds: bool = False) -> None:
        if self.is_current:
            self.clear_current()
        super().close(all_fds=all_fds)

    def make_current(self) -> None:
        if not self.is_current:
            try:
                self.old_asyncio = asyncio.get_event_loop()
            except (RuntimeError, AssertionError):
                self.old_asyncio = None  # type: ignore
            self.is_current = True
        asyncio.set_event_loop(self.asyncio_loop)

    def _clear_current_hook(self) -> None:
        if self.is_current:
            asyncio.set_event_loop(self.old_asyncio)
            self.is_current = False


def to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future:
    """Convert an `asyncio.Future` to a `tornado.concurrent.Future`.

    .. versionadded:: 4.1

    .. deprecated:: 5.0
       Tornado ``Futures`` have been merged with `asyncio.Future`,
       so this method is now a no-op.
    """
    return asyncio_future


def to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future:
    """Convert a Tornado yieldable object to an `asyncio.Future`.

    .. versionadded:: 4.1

    .. versionchanged:: 4.3
       Now accepts any yieldable object, not just
       `tornado.concurrent.Future`.

    .. deprecated:: 5.0
       Tornado ``Futures`` have been merged with `asyncio.Future`,
       so this method is now equivalent to `tornado.gen.convert_yielded`.
    """
    return convert_yielded(tornado_future)


if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
    # "Any thread" and "selector" should be orthogonal, but there's not a clean
    # interface for composing policies so pick the right base.
    _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy  # type: ignore
else:
    _BasePolicy = asyncio.DefaultEventLoopPolicy


class AnyThreadEventLoopPolicy(_BasePolicy):  # type: ignore
    """Event loop policy that allows loop creation on any thread.

    The default `asyncio` event loop policy only automatically creates
    event loops in the main threads. Other threads must create event
    loops explicitly or `asyncio.get_event_loop` (and therefore
    `.IOLoop.current`) will fail. Installing this policy allows event
    loops to be created automatically on any thread, matching the
    behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2).

    Usage::

        asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())

    .. versionadded:: 5.0

    """

    def get_event_loop(self) -> asyncio.AbstractEventLoop:
        try:
            return super().get_event_loop()
        except (RuntimeError, AssertionError):
            # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie)
            # and changed to a RuntimeError in 3.4.3.
            # "There is no current event loop in thread %r"
            loop = self.new_event_loop()
            self.set_event_loop(loop)
            return loop


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.

    Typically used via ``AddThreadSelectorEventLoop``,
    but can be attached to a running asyncio loop.
    """

    def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None:
        self._real_loop = real_loop

        # Create a thread to run the select system call. We manage this thread
        # manually so we can trigger a clean shutdown from an atexit hook. Note
        # that due to the order of operations at shutdown, only daemon threads
        # can be shut down in this way (non-daemon threads would require the
        # introduction of a new hook: https://bugs.python.org/issue41962)
        self._select_cond = threading.Condition()
        self._select_args = (
            None
        )  # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]]
        self._closing_selector = False
        self._thread = threading.Thread(
            name="Tornado selector",
            daemon=True,
            target=self._run_select,
        )
        self._thread.start()
        # Start the select loop once the loop is started.
        self._real_loop.call_soon(self._start_select)

        self._readers = {}  # type: Dict[_FileDescriptorLike, Callable]
        self._writers = {}  # type: Dict[_FileDescriptorLike, Callable]

        # Writing to _waker_w will wake up the selector thread, which
        # watches for _waker_r to be readable.
        self._waker_r, self._waker_w = socket.socketpair()
        self._waker_r.setblocking(False)
        self._waker_w.setblocking(False)
        _selector_loops.add(self)
        self.add_reader(self._waker_r, self._consume_waker)

    def __del__(self) -> None:
        # If the top-level application code uses asyncio interfaces to
        # start and stop the event loop, no objects created in Tornado
        # can get a clean shutdown notification. If we're just left to
        # be GC'd, we must explicitly close our sockets to avoid
        # logging warnings.
        _selector_loops.discard(self)
        self._waker_r.close()
        self._waker_w.close()

    def close(self) -> None:
        with self._select_cond:
            self._closing_selector = True
            self._select_cond.notify()
        self._wake_selector()
        self._thread.join()
        _selector_loops.discard(self)
        self._waker_r.close()
        self._waker_w.close()

    def _wake_selector(self) -> None:
        try:
            self._waker_w.send(b"a")
        except BlockingIOError:
            pass

    def _consume_waker(self) -> None:
        try:
            self._waker_r.recv(1024)
        except BlockingIOError:
            pass

    def _start_select(self) -> None:
        # Capture reader and writer sets here in the event loop
        # thread to avoid any problems with concurrent
        # modification while the select loop uses them.
        with self._select_cond:
            assert self._select_args is None
            self._select_args = (list(self._readers.keys()), list(self._writers.keys()))
            self._select_cond.notify()

    def _run_select(self) -> None:
        while True:
            with self._select_cond:
                while self._select_args is None and not self._closing_selector:
                    self._select_cond.wait()
                if self._closing_selector:
                    return
                assert self._select_args is not None
                to_read, to_write = self._select_args
                self._select_args = None

            # We use the simpler interface of the select module instead of
            # the more stateful interface in the selectors module because
            # this class is only intended for use on windows, where
            # select.select is the only option. The selector interface
            # does not have well-documented thread-safety semantics that
            # we can rely on so ensuring proper synchronization would be
            # tricky.
            try:
                # On windows, selecting on a socket for write will not
                # return the socket when there is an error (but selecting
                # for reads works). Also select for errors when selecting
                # for writes, and merge the results.
                #
                # This pattern is also used in
                # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317
                rs, ws, xs = select.select(to_read, to_write, to_write)
                ws = ws + xs
            except OSError as e:
                # After remove_reader or remove_writer is called, the file
                # descriptor may subsequently be closed on the event loop
                # thread. It's possible that this select thread hasn't
                # gotten into the select system call by the time that
                # happens in which case (at least on macOS), select may
                # raise a "bad file descriptor" error. If we get that
                # error, check and see if we're also being woken up by
                # polling the waker alone. If we are, just return to the
                # event loop and we'll get the updated set of file
                # descriptors on the next iteration. Otherwise, raise the
                # original error.
                if e.errno == getattr(errno, "WSAENOTSOCK", errno.EBADF):
                    rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0)
                    if rs:
                        ws = []
                    else:
                        raise
                else:
                    raise
            self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws)

    def _handle_select(
        self, rs: List["_FileDescriptorLike"], ws: List["_FileDescriptorLike"]
    ) -> None:
        for r in rs:
            self._handle_event(r, self._readers)
        for w in ws:
            self._handle_event(w, self._writers)
        self._start_select()

    def _handle_event(
        self,
        fd: "_FileDescriptorLike",
        cb_map: Dict["_FileDescriptorLike", Callable],
    ) -> None:
        try:
            callback = cb_map[fd]
        except KeyError:
            return
        callback()

    def add_reader(
        self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any
    ) -> None:
        self._readers[fd] = functools.partial(callback, *args)
        self._wake_selector()

    def add_writer(
        self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any
    ) -> None:
        self._writers[fd] = functools.partial(callback, *args)
        self._wake_selector()

    def remove_reader(self, fd: "_FileDescriptorLike") -> None:
        del self._readers[fd]
        self._wake_selector()

    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)

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user