From d226c9dda847127e40010f61d2612efb410bdabc Mon Sep 17 00:00:00 2001 From: Gonzalo Casas Date: Thu, 25 Jun 2026 01:25:01 +0200 Subject: [PATCH] Wake the reactor when (re)connecting on the Twisted transport `AutobahnRosBridgeClientFactory.connect()` registered the connector by calling `connectWS()` directly from the caller's thread, which does not wake the Twisted reactor. On the first connection this is harmless because the reactor is started (and processes the pending connector) right after. But on a later reconnect with the reactor already running, an idle reactor sits blocked in `select()` with no file descriptors and never notices the connector added from another thread, so the connection never completes and `run()` times out. This surfaced when reconnecting after a disconnect: for roughly `closeHandshakeTimeout` (5s) the closing connection keeps the reactor cycling, so a quick reconnect works, but waiting longer leaves the reactor idle and the reconnect hangs until timeout (e.g. toggling a Grasshopper ROS client off, waiting, and back on). Schedule the connection through `reactor.callFromThread()` (as `send_message`/`send_close` already do), which wakes the reactor. It is also safe before the reactor has started, since the call is queued and runs once it does. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.rst | 6 ++++++ src/roslibpy/comm/comm_autobahn.py | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6dca910..b483d3c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,12 @@ Unreleased **Fixed** +* Fixed reconnection on the Twisted transport failing after a connection had + been idle for more than a few seconds: ``connect`` now schedules the + connection through the reactor thread so an idle reactor is woken to service + it (previously the new connector was added from another thread and the + reactor, blocked in ``select()``, never noticed it). + **Deprecated** **Removed** diff --git a/src/roslibpy/comm/comm_autobahn.py b/src/roslibpy/comm/comm_autobahn.py index 3a830fd..bbf6cc0 100644 --- a/src/roslibpy/comm/comm_autobahn.py +++ b/src/roslibpy/comm/comm_autobahn.py @@ -71,6 +71,16 @@ def __init__(self, *args, **kwargs): def connect(self): """Establish WebSocket connection to the ROS server defined for this factory.""" + # Route the connection setup through the reactor thread. Calling + # ``connectWS`` directly only happens to work while the reactor is busy; + # once it is idle (e.g. more than ``closeHandshakeTimeout`` seconds after + # a previous disconnect) it sits blocked in ``select()`` and never + # notices a connector added from another thread, so the connection times + # out. ``callFromThread`` wakes it, and is also safe before the reactor + # has started (the call is queued and runs once it does). + reactor.callFromThread(self._connect) + + def _connect(self): self.connector = connectWS(self) @property