Chromium style's rules regarding static and global variables make the following idiom attractive:
// This is // * thread-safe (since C++11). Initialization happens at most once. // * compliant with Chromium style, as destructor never runs. NonTrivialFoo& GetFoo() { static base::NoDestructor<NonTrivialFoo> local; return *local; }
However, the Windows C Runtime (CRT) forces the PartitionAlloc team to use this idiom with great care. This is because initializing a (nontrivial) function-local static object requires taking a lock (entering a critical section).
Therefore, it's imperative that control flow never passes into initializing a static local before Windows CRT is fully initialized.
constinit
and constexpr
static locals are exempt from this rule. Since they are initialized by the compiler, there's no dangerous interaction with Windows CRT.N.B. the first few items in this list (allocator shim and ThreadCache
) demonstrate successful workarounds for this puzzle.
These Google-internal slides describe the time we hit this when implementing PartitionAlloc-Everywhere.
This bug was the background for the slides above and additionally chronicles other function-local statics rooted out to get PA-E working on Windows.
We hit this again in 2020 in ThreadCache
initialization.
We hit this again in 2022 in attempting to change AddressPoolManager
initialization. This was later fixed without using a static local.
We hit this again in 2024 in trying to set up a freelist experiment.