folly SingletonThreadLocal class use two "c11 thread_local" and one "ThreadLocal" to support fast access and destruction dependency problems.
But I could not understand the destruction order of "thread_local and ThreadLocal".
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& get() {
if (kIsMobile) {
return getWrapper();
}
static thread_local LocalCache cache;
return FOLLY_LIKELY(!!cache.cache) ? *cache.cache : getSlow(cache);
}
FOLLY_NOINLINE static Wrapper& getSlow(LocalCache& cache) {
if (threadlocal_detail::StaticMetaBase::dying()) {
return getWrapper();
}
static thread_local LocalLifetime lifetime;
lifetime.track(cache); // idempotent
return FOLLY_LIKELY(!!cache.cache) ? *cache.cache : getWrapper();
}
struct LocalLifetime {
~LocalLifetime() {
auto& wrapper = getWrapper();
auto& lifetimes = wrapper.lifetimes[this]; //1. thread wrapper may be destructed.
for (auto cache : lifetimes) {
auto const it = wrapper.caches.find(cache);
if (!--it->second) {
wrapper.caches.erase(it);
cache->cache = nullptr;
}
}
wrapper.lifetimes.erase(this);
}
...
}
struct Wrapper {
~Wrapper() {
for (auto& kvp : caches) { //cache may be destruct
kvp.first->cache = nullptr; //2. thread cache maybe destructed.
}
}
}
please check the comment 1, 2. the destruction ordered of LocalLifetime LocalCache, and ThreadLocal is undefined, the code may access the destructed memory, it is surprising.