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
|
SLAVE_ID = 0x10 # all four devices share slave id 16
|
||||||
BAUDRATE = 9600
|
BAUDRATE = 9600
|
||||||
SERIAL_TIMEOUT = 2.0 # seconds
|
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
|
return reads
|
||||||
|
|
||||||
def close(self):
|
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:
|
try:
|
||||||
self.inst.serial.close()
|
self.inst.serial.close()
|
||||||
logger.info("%s disconnected", self.name)
|
logger.info("%s disconnected", self.name)
|
||||||
|
|||||||
Reference in New Issue
Block a user