From dd0ae2011cbfb3ae1d8cc503958bbeb98ec86fbc Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Wed, 23 Sep 2026 11:26:38 +0200 Subject: [PATCH] fix(test): close TSAN race in WaitForSocketFile helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WaitForSocketFile() used stat() to detect when the server's socket file appeared, but that only proves bind() completed — not listen(). Under TSAN instrumentation the window between bind() and listen() widens enough for the subsequent connect() in the test to hit ECONNREFUSED. Replace the stat()-based check with an actual connect() probe that retries until the server is accepting connections, closing the race. The probe connection is immediately closed without sending data, so the server treats it as a closed-by-peer with no session — no factory calls or session counters are affected. See: https://github.com/eclipse-score/logging/actions/runs/35837155339/job/107103240555 for a example where this race condition happened. Co-Authored-By: Claude Opus 4.6 --- .../ut/ut_logging/test_unix_domain_server.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/score/datarouter/test/ut/ut_logging/test_unix_domain_server.cpp b/score/datarouter/test/ut/ut_logging/test_unix_domain_server.cpp index 79901cebb..8734bc5e9 100644 --- a/score/datarouter/test/ut/ut_logging/test_unix_domain_server.cpp +++ b/score/datarouter/test/ut/ut_logging/test_unix_domain_server.cpp @@ -270,7 +270,8 @@ UnixDomainSockAddr MakeTempAddrAbstractFalse(std::string& name) return UnixDomainSockAddr(name, /*isAbstract=*/false); } -/// Block until the filesystem socket at \p path exists (i.e. bind() completed). +/// Block until the server at \p path is accepting connections (i.e. listen() completed). +/// A successful non-blocking connect() proves the server passed both bind() and listen(). /// Returns true on success, false on timeout. bool WaitForSocketFile(const std::string& path, std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) { @@ -278,10 +279,18 @@ bool WaitForSocketFile(const std::string& path, std::chrono::milliseconds timeou auto deadline = std::chrono::steady_clock::now() + timeout; while (std::chrono::steady_clock::now() < deadline) { - struct stat st{}; - if (::stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFSOCK) != 0) + int fd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (fd >= 0) { - return true; + sockaddr_un su{}; + su.sun_family = AF_UNIX; + std::strncpy(su.sun_path, path.c_str(), sizeof(su.sun_path) - 1); + if (::connect(fd, reinterpret_cast(&su), sizeof(su)) == 0) + { + ::close(fd); + return true; + } + ::close(fd); } std::this_thread::sleep_for(kPollInterval); }