]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Make exception/signal handlers idempotent
authorMichał Kępień <michal@isc.org>
Sun, 21 Dec 2025 05:25:56 +0000 (06:25 +0100)
committerMichał Kępień <michal@isc.org>
Sun, 21 Dec 2025 05:25:56 +0000 (06:25 +0100)
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.

bin/tests/system/isctest/asyncserver.py

index 98fec6b6634e1330e97e1ac766cf54620af6c711..6ec3b00f10d41a068bf5d3f449a725cb98a4b51f 100644 (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: