From f744f2044af010df233e59bf6b5d846f34979a20 Mon Sep 17 00:00:00 2001 From: mskwt Date: Wed, 20 May 2026 21:07:04 +0900 Subject: [PATCH] fix(streaming): make ServerSession::CloseNow idempotent CloseNow() could be entered more than once for the same TCP server session. The deadline timer firing, an async read/write completing with an error, and an explicit Close() can all race, and each entry invoked _on_closed(). That ran DisconnectSession twice on the stream state: - in debug builds it tripped the DEBUG_ASSERT in MultiStreamState::DisconnectSession (session == _session.load()); - in release builds it corrupted the active-session bookkeeping, which surfaced later as dropped stream broadcasts and get_world()/sensor stalls after a number of connect/disconnect cycles. Guard CloseNow() with an atomic flag so the close path (timer cancel, socket shutdown, _on_closed) runs exactly once per session. Reproduced with two clients on the world-snapshot stream: hard-killing one while a snapshot write is in flight evicts the other on stock 0.9.15 (its wait_for_tick stalls on the first cycle); with this change the surviving client keeps ticking 30/30 cycles. Co-Authored-By: Claude Opus 4.7 --- .../carla/streaming/detail/tcp/ServerSession.cpp | 10 ++++++++++ .../source/carla/streaming/detail/tcp/ServerSession.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/LibCarla/source/carla/streaming/detail/tcp/ServerSession.cpp b/LibCarla/source/carla/streaming/detail/tcp/ServerSession.cpp index f162a2e8637..71fb24a8e0a 100644 --- a/LibCarla/source/carla/streaming/detail/tcp/ServerSession.cpp +++ b/LibCarla/source/carla/streaming/detail/tcp/ServerSession.cpp @@ -135,6 +135,16 @@ namespace tcp { } void ServerSession::CloseNow(boost::system::error_code ec) { + // Guard against double-close. CloseNow() can be reached more than once for + // the same session: the deadline timer firing, an async read/write + // completing with an error, and an explicit Close() can all race. Without + // this guard the session invokes _on_closed twice, which calls + // DisconnectSession twice on the stream state, tripping the DEBUG_ASSERT in + // MultiStreamState::DisconnectSession (and corrupting session bookkeeping + // in release builds). + if (_is_closed.exchange(true)) { + return; + } _deadline.cancel(); if (!ec) { diff --git a/LibCarla/source/carla/streaming/detail/tcp/ServerSession.h b/LibCarla/source/carla/streaming/detail/tcp/ServerSession.h index 5cc0f4ddd36..1cb54d26a02 100644 --- a/LibCarla/source/carla/streaming/detail/tcp/ServerSession.h +++ b/LibCarla/source/carla/streaming/detail/tcp/ServerSession.h @@ -22,6 +22,8 @@ # pragma clang diagnostic pop #endif +#include + namespace carla { namespace streaming { namespace detail { @@ -81,6 +83,8 @@ namespace tcp { callback_function_type _on_closed; bool _is_writing = false; + + std::atomic_bool _is_closed{false}; }; } // namespace tcp