Make exception/signal handlers idempotent

Calling asyncio.Future.set_exception() or asyncio.Future.set_result()
more than once for a given Future object raises an
asyncio.InvalidStateError exception.

In the case of AsyncServer:

  - it is enough to capture the first exception raised by higher-level
    logic as no exceptions at all are expected to be raised in the first
    place,

  - no distinction is made between SIGINT and SIGTERM; the only purpose
    of the signal handler is to make the server exit cleanly.

Given the above, make both AsyncServer._handle_exception() and
AsyncServer._signal_done() idempotent by ignoring
asyncio.InvalidStateError exceptions raised by the relevant
asyncio.Future.set_*() calls.

(cherry picked from commit 0ec94e501a)
This commit is contained in:
Michał Kępień 2025-12-21 06:25:56 +01:00 committed by Michał Kępień (GitLab job 6657129)
parent 1ac8e80916
commit f2b8863691

View file

@ -198,7 +198,10 @@ class AsyncServer:
) -> None:
assert self._work_done
exception = context.get("exception", RuntimeError(context["message"]))
self._work_done.set_exception(exception)
try:
self._work_done.set_exception(exception)
except asyncio.InvalidStateError:
pass
def _setup_signals(self) -> None:
loop = self._get_asyncio_loop()
@ -207,7 +210,10 @@ class AsyncServer:
def _signal_done(self) -> None:
assert self._work_done
self._work_done.set_result(True)
try:
self._work_done.set_result(True)
except asyncio.InvalidStateError:
pass
async def _listen_udp(self) -> None:
if not self._udp_handler: