mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-06-14 05:06:53 +00:00
G5: F05 — track fire-and-forget asyncio.create_task references
Why: Python's asyncio holds only weak references to tasks created via create_task(). Under GC pressure (especially Python < 3.11), unretained tasks can be silently cancelled mid-execution, and any exceptions are swallowed as "Task exception was never retrieved." Seven call sites across TCPConnection, BLEConnection, SerialConnection, and EventDispatcher used fire-and-forget create_task with no stored reference. Fix: introduce _background_tasks set and _spawn_background() helper on each class, following the standard pattern from the asyncio docs (task.add_done_callback(set.discard)). Refs: Forensics report finding F05
This commit is contained in:
@@ -134,6 +134,14 @@ class EventDispatcher:
|
||||
self.subscriptions: List[Subscription] = []
|
||||
self.running = False
|
||||
self._task = None
|
||||
self._background_tasks: set[asyncio.Task] = set()
|
||||
|
||||
def _spawn_background(self, coro) -> asyncio.Task:
|
||||
"""Create a tracked background task (prevents GC of fire-and-forget tasks)."""
|
||||
task = asyncio.create_task(coro)
|
||||
self._background_tasks.add(task)
|
||||
task.add_done_callback(self._background_tasks.discard)
|
||||
return task
|
||||
|
||||
def subscribe(
|
||||
self,
|
||||
@@ -197,7 +205,7 @@ class EventDispatcher:
|
||||
# returns - avoids the race where create_task schedules the callback after
|
||||
# the waiter has already timed out with done=set().
|
||||
if asyncio.iscoroutinefunction(subscription.callback):
|
||||
asyncio.create_task(self._execute_callback(subscription.callback, event.clone()))
|
||||
self._spawn_background(self._execute_callback(subscription.callback, event.clone()))
|
||||
else:
|
||||
try:
|
||||
subscription.callback(event.clone())
|
||||
|
||||
Reference in New Issue
Block a user