fix(marker): avoid reentrant-lock deadlock when uBridge starts lazily

_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.
This commit is contained in:
YueGuobin 2026-07-15 01:27:42 +08:00
parent 08f4b5ae0d
commit 45d18de8af
No known key found for this signature in database

View File

@ -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",