Bound serial close so a dead port cannot hang the run
When a USB-serial adapter disappears mid-run, writes to the open handle fail with PermissionError, but serial.close() can block forever. Cleanup then wedged after the second port closed and Ctrl+C could not break out, because the main thread was stuck inside a blocking Windows call. Close on a daemon thread and give up after CLOSE_TIMEOUT_S, so an abandoned port can never hold up the exit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,7 @@ import minimalmodbus
|
||||
SLAVE_ID = 0x10 # all four devices share slave id 16
|
||||
BAUDRATE = 9600
|
||||
SERIAL_TIMEOUT = 2.0 # seconds
|
||||
CLOSE_TIMEOUT_S = 3.0 # give up on a port that will not close
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -217,6 +218,17 @@ class Device:
|
||||
return reads
|
||||
|
||||
def close(self):
|
||||
# A USB adapter unplugged mid-run wedges serial.close() forever, and
|
||||
# Ctrl+C cannot break a blocking Windows call. Close on a daemon
|
||||
# thread so a dead port can never hold up the exit.
|
||||
closer = threading.Thread(target=self._close_serial, daemon=True)
|
||||
closer.start()
|
||||
closer.join(CLOSE_TIMEOUT_S)
|
||||
if closer.is_alive():
|
||||
logger.warning("%s: %s did not close in %.0fs, abandoning it",
|
||||
self.name, self.port, CLOSE_TIMEOUT_S)
|
||||
|
||||
def _close_serial(self):
|
||||
try:
|
||||
self.inst.serial.close()
|
||||
logger.info("%s disconnected", self.name)
|
||||
|
||||
Reference in New Issue
Block a user