Context
While reviewing PR #64 (send-queue hang), we found that a single unguarded exception
inside a DLL's async_job_thread implementation is enough to silently kill the whole
protocol job thread for the lifetime of the ElectronicControlUnit. This actually
happened in PR #64's original send-queue patch (an uncaught StopIteration), and was
fixed there — but the underlying gap in electronic_control_unit.py remains.
_protocol_job_thread (j1939/electronic_control_unit.py) calls
self.j1939_dll.async_job_thread(now) in a tight loop with no try/except:
while not self._job_thread_end.is_set():
now = time.monotonic()
next_wakeup = self.j1939_dll.async_job_thread(now)
...
If this call raises for any reason, the while loop exits and the thread terminates
silently — no log output, no indication anything went wrong. All TP/BAM timeout
handling and other protocol-level bookkeeping that depends on this thread running
stops permanently.
This is inconsistent with the dispatch thread, which already wraps its callback
in try/except Exception: logger.exception(...) (see around
electronic_control_unit.py:576-579).
Proposal
Wrap the async_job_thread call the same way the dispatch thread wraps notify(...):
log the exception (so failures are visible) rather than letting it silently kill the
thread. Whether the loop should continue after such an exception (best-effort defense
in depth) or still terminate (fail loud) is a design call — logging is the minimum bar.
Notes
Pre-existing gap on master, not introduced by PR #64 — that PR just demonstrated how
easily it can bite in practice.
Context
While reviewing PR #64 (send-queue hang), we found that a single unguarded exception
inside a DLL's
async_job_threadimplementation is enough to silently kill the wholeprotocol job thread for the lifetime of the
ElectronicControlUnit. This actuallyhappened in PR #64's original send-queue patch (an uncaught
StopIteration), and wasfixed there — but the underlying gap in
electronic_control_unit.pyremains._protocol_job_thread(j1939/electronic_control_unit.py) callsself.j1939_dll.async_job_thread(now)in a tight loop with notry/except:If this call raises for any reason, the
whileloop exits and the thread terminatessilently — no log output, no indication anything went wrong. All TP/BAM timeout
handling and other protocol-level bookkeeping that depends on this thread running
stops permanently.
This is inconsistent with the dispatch thread, which already wraps its callback
in
try/except Exception: logger.exception(...)(see aroundelectronic_control_unit.py:576-579).Proposal
Wrap the
async_job_threadcall the same way the dispatch thread wrapsnotify(...):log the exception (so failures are visible) rather than letting it silently kill the
thread. Whether the loop should continue after such an exception (best-effort defense
in depth) or still terminate (fail loud) is a design call — logging is the minimum bar.
Notes
Pre-existing gap on
master, not introduced by PR #64 — that PR just demonstrated howeasily it can bite in practice.