From d4581a8e136d7ef0b89152df6de4cc7444e8b18c Mon Sep 17 00:00:00 2001 From: Matthew Wolter Date: Sun, 12 Apr 2026 03:56:28 -0700 Subject: [PATCH] =?UTF-8?q?G5:=20F07=20=E2=80=94=20await=20in-flight=20asy?= =?UTF-8?q?nc=20callbacks=20before=20stop()=20returns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why: EventDispatcher._process_events() calls task_done() on the queue immediately after spawning async callback tasks. await queue.join() in stop() therefore returns as soon as all items are marked done, even if their async callbacks are still executing. Any caller that does "await dispatcher.stop(); cleanup()" could race with still-running callbacks. Fix: after queue.join(), gather all tracked background tasks before cancelling the dispatch loop. Refs: Forensics report finding F07 --- src/meshcore/events.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/meshcore/events.py b/src/meshcore/events.py index 2f23338..dbf7056 100644 --- a/src/meshcore/events.py +++ b/src/meshcore/events.py @@ -236,6 +236,10 @@ class EventDispatcher: self.running = False if self._task: await self.queue.join() + # Wait for any in-flight async callbacks to complete before + # tearing down (F07: task_done fires before callbacks finish). + if self._background_tasks: + await asyncio.gather(*self._background_tasks, return_exceptions=True) self._task.cancel() try: await self._task