From 1b0f0e3d7d03155da1cf9769a847874d559e57e3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 12 Sep 2020 08:50:18 +0200 Subject: [PATCH] bpo-39651: Fix asyncio proactor _write_to_self() (GH-22197) Fix a race condition in the call_soon_threadsafe() method of asyncio.ProactorEventLoop: do nothing if the self-pipe socket has been closed. --- Lib/asyncio/proactor_events.py | 11 ++++++++++- Lib/asyncio/selector_events.py | 18 ++++++++++-------- .../2020-09-11-12-38-55.bpo-39651.JMp9l2.rst | 3 +++ 3 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 4670bd683ab..45c11ee4b48 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -793,8 +793,17 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): f.add_done_callback(self._loop_self_reading) def _write_to_self(self): + # This may be called from a different thread, possibly after + # _close_self_pipe() has been called or even while it is + # running. Guard for self._csock being None or closed. When + # a socket is closed, send() raises OSError (with errno set to + # EBADF, but let's not rely on the exact error code). + csock = self._csock + if csock is None: + return + try: - self._csock.send(b'\0') + csock.send(b'\0') except OSError: if self._debug: logger.debug("Fail to write a null byte into the " diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 8495a7901cd..59cb6b1babe 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -133,14 +133,16 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): # a socket is closed, send() raises OSError (with errno set to # EBADF, but let's not rely on the exact error code). csock = self._csock - if csock is not None: - try: - csock.send(b'\0') - except OSError: - if self._debug: - logger.debug("Fail to write a null byte into the " - "self-pipe socket", - exc_info=True) + if csock is None: + return + + try: + csock.send(b'\0') + except OSError: + if self._debug: + logger.debug("Fail to write a null byte into the " + "self-pipe socket", + exc_info=True) def _start_serving(self, protocol_factory, sock, sslcontext=None, server=None, backlog=100, diff --git a/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst b/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst new file mode 100644 index 00000000000..78dcff13700 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-11-12-38-55.bpo-39651.JMp9l2.rst @@ -0,0 +1,3 @@ +Fix a race condition in the ``call_soon_threadsafe()`` method of +``asyncio.ProactorEventLoop``: do nothing if the self-pipe socket has been +closed.