From 88d63674914c647a0e9dc460bd698683b69feecd Mon Sep 17 00:00:00 2001 From: Latent Prion Date: Sun, 12 Jul 2026 06:10:41 -0400 Subject: [PATCH] probe: barrier TLS init until make_shared arms shared_from_this. PuppeteerThread starts its OS thread in the constructor, so initializeTls() could race and throw bad_weak_ptr; wait for the harness barrier first. --- probe/probeComponentThread.cpp | 46 +++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/probe/probeComponentThread.cpp b/probe/probeComponentThread.cpp index 3487f44..80ab90a 100644 --- a/probe/probeComponentThread.cpp +++ b/probe/probeComponentThread.cpp @@ -1,6 +1,8 @@ #include +#include #include +#include #include @@ -25,12 +27,48 @@ public: } }; +/** EXPLANATION: + * PuppeteerThread starts its std::thread inside the constructor, but + * enable_shared_from_this::weak_this is only armed after make_shared returns. + * Without a barrier, initializeTls()'s shared_from_this() races and throws + * std::bad_weak_ptr. DedicatedIoThread uses the same handshake. + */ +struct ProbeThreadStartupState +{ + std::mutex mutex; + std::condition_variable condition; + bool allowInitialization = false; +}; + +void waitForProbeThreadStartupPermission( + const std::shared_ptr& startupState) +{ + std::unique_lock lock(startupState->mutex); + startupState->condition.wait( + lock, + [&startupState]() { return startupState->allowInitialization; }); +} + +void releaseProbeThreadStartupBarrier( + const std::shared_ptr& startupState) +{ + { + std::lock_guard guard(startupState->mutex); + startupState->allowInitialization = true; + } + + startupState->condition.notify_all(); +} + void probePuppeteerMain( const sscl::PuppeteerThread::EntryFnArguments& args, const std::function&)>& work, - std::promise& donePromise) + std::promise& donePromise, + const std::shared_ptr& startupState) { + waitForProbeThreadStartupPermission(startupState); + sscl::PuppeteerThread& thr = args.usableBeforeJolt; thr.initializeTls(); sscl::ComponentThread::setPuppeteerThreadId(PROBE_PUPPETEER_THREAD_ID); @@ -73,21 +111,23 @@ void ProbeComponentThreadHarness::runSync( { std::promise donePromise; std::future doneFuture = donePromise.get_future(); + auto startupState = std::make_shared(); std::shared_ptr runThread = std::make_shared( PROBE_PUPPETEER_THREAD_ID, threadName, - [&work, &donePromise]( + [&work, &donePromise, startupState]( const sscl::PuppeteerThread::EntryFnArguments& args) { - probePuppeteerMain(args, work, donePromise); + probePuppeteerMain(args, work, donePromise, startupState); }, *dummyComponent, nullptr); dummyComponent->thread = runThread; lastComponentThread = runThread; + releaseProbeThreadStartupBarrier(startupState); runThread->thread.join(); std::exception_ptr probeException = doneFuture.get();