From 2bb5a006df54480fe039e0c34d1394a53283eb55 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 24 Aug 2022 19:03:13 -0400 Subject: [PATCH] Run cleanup functions synchronously if the event loop is stopping --- serial_asyncio/__init__.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/serial_asyncio/__init__.py b/serial_asyncio/__init__.py index 08d9fcb..bf40f26 100644 --- a/serial_asyncio/__init__.py +++ b/serial_asyncio/__init__.py @@ -395,6 +395,20 @@ def _abort(self, exc): self._remove_writer() # Pending buffered data will not be written self._loop.create_task(self._call_connection_lost(exc)) + async def _try_to_run_in_executor(self, executor, func, *args): + """Runs `func` synchronously if the executor is stopped.""" + + try: + coro = self._loop.run_in_executor(executor, func, *args) + except RuntimeError: + # This error is seen only when the event loop is shutting down and the + # executor has already been shut down first. Since the cleanup methods are + # usually fast and the loop is stopping anyways, we have no choice but to + # run them synchronously. + return func(*args) + else: + return await coro + async def _call_connection_lost(self, exc): """Close the connection. @@ -405,7 +419,7 @@ async def _call_connection_lost(self, exc): assert not self._has_writer assert not self._has_reader try: - await self._loop.run_in_executor(None, self._serial.flush) + await self._try_to_run_in_executor(None, self._serial.flush) except (serial.SerialException if os.name == "nt" else termios.error): # ignore serial errors which may happen if the serial device was # hot-unplugged. @@ -415,7 +429,7 @@ async def _call_connection_lost(self, exc): self._protocol.connection_lost(exc) finally: self._write_buffer.clear() - await self._loop.run_in_executor(None, self._serial.close) + await self._try_to_run_in_executor(None, self._serial.close) self._serial = None self._protocol = None self._loop = None