diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 2551201d..d8ded86d 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -1425,6 +1425,22 @@ std::atomic init_order(102) RpInitLock( 0 ); thread_local bool RpThreadInitDone = false; thread_local bool RpThreadShutdown = false; moodycamel::ConcurrentQueue init_order(103) s_queue( QueuePrealloc ); + +# ifndef _MSC_VER +// An instrumented shared object may emit zones from its static initializers, which the +// dynamic loader runs before any of the executable's constructors, including the +// priority-ordered constructor of s_queue above. The main thread producer token (s_token) +// is then lazily created against the zero-initialized queue memory, and the queue +// constructor subsequently orphans it, making all zones emitted on the main thread +// invisible to the consumer. Re-adopt such a producer here. If no zones were emitted up +// to this point, this only triggers construction of s_token, which is a no-op repair. +struct EarlyMainThreadTokenRepair +{ + EarlyMainThreadTokenRepair() { if( s_token.ptr ) s_queue.readopt_orphaned_producer( s_token.ptr ); } +}; +static EarlyMainThreadTokenRepair init_order(104) s_earlyMainThreadTokenRepair; +# endif + std::atomic init_order(104) s_lockCounter( 0 ); std::atomic init_order(104) s_gpuCtxCounter( 0 ); diff --git a/public/client/tracy_concurrentqueue.h b/public/client/tracy_concurrentqueue.h index d16b168d..2ae31cc9 100644 --- a/public/client/tracy_concurrentqueue.h +++ b/public/client/tracy_concurrentqueue.h @@ -1210,6 +1210,21 @@ private: return static_cast(token.producer); } + // If a producer token is created before the constructor of a statically allocated + // queue runs (which may happen due to the undefined order of static initialization + // across module boundaries), the constructor will orphan it by resetting the + // producer list. Such a producer is functional, as producer creation works on the + // zero-initialized queue memory, but the consumer is not able to see the data it + // enqueues. This method links the producer back into the list. + bool readopt_orphaned_producer(ExplicitProducer* producer) + { + for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr == static_cast(producer)) return false; + } + add_producer(static_cast(producer)); + return true; + } + private: //////////////////////////////////