From 45d18de8af1a5a46fd760789da01c964e22ce1c9 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Wed, 15 Jul 2026 01:27:42 +0800 Subject: [PATCH] fix(marker): avoid reentrant-lock deadlock when uBridge starts lazily MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _start_ubridge configures the marker sink via _ubridge_configure_marker_sink, which used _ubridge_send. But _start_ubridge itself is reached THROUGH _ubridge_send when uBridge is started lazily — e.g. linking a stopped node. _ubridge_send's @locking lock (___ubridge_send_lock) is non-reentrant, so the nested _ubridge_send('marker sink') deadlocked forever, making the NIO create POST time out after 120s. Started nodes never hit this (uBridge already running, no _start_ubridge), so only stopped-node links (notably dynamips) hung. _ubridge_configure_marker_sink now talks to self._ubridge_hypervisor.send directly: it runs inside _start_ubridge right after connect(), so uBridge is already up and the raw send is safe, with no reentrant lock acquisition. --- gns3server/compute/base_node.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/gns3server/compute/base_node.py b/gns3server/compute/base_node.py index 24d7813ae..df92de0aa 100644 --- a/gns3server/compute/base_node.py +++ b/gns3server/compute/base_node.py @@ -958,9 +958,17 @@ class BaseNode: manager = MarkerManager.instance() if not manager.running or not manager.host or not manager.port: return + if self._ubridge_hypervisor is None: + return try: - await self._ubridge_send(f"marker sink {manager.host} {manager.port}") - await self._ubridge_send(f"marker node {self._id}") + # Talk to the hypervisor directly, NOT via _ubridge_send: this runs + # inside _start_ubridge, which is reached THROUGH _ubridge_send when + # uBridge starts lazily (e.g. linking a stopped node). _ubridge_send's + # lock is non-reentrant, so calling it again here would deadlock on + # the held ___ubridge_send_lock. uBridge is already running and + # connected at this point, so the raw hypervisor send is safe. + await self._ubridge_hypervisor.send(f"marker sink {manager.host} {manager.port}") + await self._ubridge_hypervisor.send(f"marker node {self._id}") except UbridgeError: log.warning( "uBridge does not support the marker module; traffic insight disabled for node %r",