0

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.

Mat
  • 202,337
  • 40
  • 393
  • 406
yuandaxing
  • 177
  • 1
  • 8
  • The destruction order of `cache` and `lifetime` is well-defined (unless a thread calls `getSlow` directly, bypassing `get`). `cache` is constructed when a thread first reaches its declaration in `get` - which happens before `lifetime` is constructed when the thread in turn reaches its declaration in `getSlow`. Then they are guaranteed to be destroyed in the reverse order of construction. – Igor Tandetnik Aug 04 '21 at 14:27
  • In the Wrapper destructor, it modify cache, how to make sure ThreadLocal destruct before LocalCache? – yuandaxing Aug 06 '21 at 15:13
  • I don't see `Wrapper` class used anywhere in the code shown. I can only guess that it's perhaps used in `getWrapper()` somehow, but I can't comment on code I can't see. – Igor Tandetnik Aug 06 '21 at 15:31
  • source code:https://github.com/facebook/folly/blob/master/folly/SingletonThreadLocal.h and it reference a lot of other source files – yuandaxing Aug 06 '21 at 22:59
  • @Mat, thank you very much, I think i got the anwser. Folly::ThreadLocal use pthread_key_t internally(linux platform), and C11 thread_local and pthread_key_t destruction order is undefined. and no matter which order, LocalLifeTime destruct first or pthread_key_t, the code ensure destruct properly. – yuandaxing Aug 08 '21 at 04:08

0 Answers0