X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FThreadLocal.h;h=3565c210a25e0279bd6d1ce614cf684a8fe3852e;hb=cffc8a776b21a070f0af6ed0748452c329ad6e5f;hp=c1181935465e72cd894359ca49bfbcb5bc8b2dfa;hpb=5c77fedbef46995a71ffa268c9fcaf49efddd01b;p=folly.git diff --git a/folly/ThreadLocal.h b/folly/ThreadLocal.h index c1181935..3565c210 100644 --- a/folly/ThreadLocal.h +++ b/folly/ThreadLocal.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,10 @@ * objects of a parent. accessAllThreads() initializes an accessor which holds * a global lock *that blocks all creation and destruction of ThreadLocal * objects with the same Tag* and can be used as an iterable container. + * accessAllThreads() can race with destruction of thread-local elements. We + * provide a strict mode which is dangerous because it requires the access lock + * to be held while destroying thread-local elements which could cause + * deadlocks. We gate this mode behind the AccessModeStrict template parameter. * * Intended use is for frequent write, infrequent read data access patterns such * as counters. @@ -34,56 +38,50 @@ * @author Spencer Ahrens (sahrens) */ -#ifndef FOLLY_THREADLOCAL_H_ -#define FOLLY_THREADLOCAL_H_ +#pragma once -#include "folly/Portability.h" #include -#include "folly/Likely.h" +#include +#include +#include +#include #include - -// Use noexcept on gcc 4.6 or higher -#undef FOLLY_NOEXCEPT -#ifdef __GNUC__ -# ifdef HAVE_FEATURES_H -# include -# if __GNUC_PREREQ(4,6) -# define FOLLY_NOEXCEPT noexcept -# define FOLLY_ASSERT(x) x -# endif -# endif -#endif - -#ifndef FOLLY_NOEXCEPT -# define FOLLY_NOEXCEPT -# define FOLLY_ASSERT(x) /**/ -#endif +#include namespace folly { enum class TLPDestructionMode { THIS_THREAD, ALL_THREADS }; +struct AccessModeStrict {}; } // namespace -#include "folly/detail/ThreadLocalDetail.h" +#include namespace folly { -template class ThreadLocalPtr; +template +class ThreadLocalPtr; -template +template class ThreadLocal { public: - ThreadLocal() { } + constexpr ThreadLocal() : constructor_([]() { + return new T(); + }) {} + + explicit ThreadLocal(std::function constructor) : + constructor_(constructor) { + } T* get() const { T* ptr = tlp_.get(); - if (UNLIKELY(ptr == nullptr)) { - ptr = new T(); - tlp_.reset(ptr); + if (LIKELY(ptr != nullptr)) { + return ptr; } - return ptr; + + // separated new item creation out to speed up the fast path. + return makeTlp(); } T* operator->() const { @@ -98,7 +96,7 @@ class ThreadLocal { tlp_.reset(newPtr); } - typedef typename ThreadLocalPtr::Accessor Accessor; + typedef typename ThreadLocalPtr::Accessor Accessor; Accessor accessAllThreads() const { return tlp_.accessAllThreads(); } @@ -112,7 +110,14 @@ class ThreadLocal { ThreadLocal(const ThreadLocal&) = delete; ThreadLocal& operator=(const ThreadLocal&) = delete; - mutable ThreadLocalPtr tlp_; + T* makeTlp() const { + auto ptr = constructor_(); + tlp_.reset(ptr); + return ptr; + } + + mutable ThreadLocalPtr tlp_; + std::function constructor_; }; /* @@ -133,22 +138,30 @@ class ThreadLocal { * We use a single global pthread_key_t per Tag to manage object destruction and * memory cleanup upon thread exit because there is a finite number of * pthread_key_t's available per machine. + * + * NOTE: Apple platforms don't support the same semantics for __thread that + * Linux does (and it's only supported at all on i386). For these, use + * pthread_setspecific()/pthread_getspecific() for the per-thread + * storage. Windows (MSVC and GCC) does support the same semantics + * with __declspec(thread) */ -template +template class ThreadLocalPtr { + private: + typedef threadlocal_detail::StaticMeta StaticMeta; + public: - ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta::create()) { } + constexpr ThreadLocalPtr() : id_() {} - ThreadLocalPtr(ThreadLocalPtr&& other) : id_(other.id_) { - other.id_ = 0; + ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : + id_(std::move(other.id_)) { } ThreadLocalPtr& operator=(ThreadLocalPtr&& other) { assert(this != &other); destroy(); - id_ = other.id_; - other.id_ = 0; + id_ = std::move(other.id_); return *this; } @@ -157,7 +170,8 @@ class ThreadLocalPtr { } T* get() const { - return static_cast(threadlocal_detail::StaticMeta::get(id_).ptr); + threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_); + return static_cast(w.ptr); } T* operator->() const { @@ -168,45 +182,84 @@ class ThreadLocalPtr { return *get(); } + T* release() { + threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_); + + return static_cast(w.release()); + } + void reset(T* newPtr = nullptr) { - threadlocal_detail::ElementWrapper& w = - threadlocal_detail::StaticMeta::get(id_); - if (w.ptr != newPtr) { - w.dispose(TLPDestructionMode::THIS_THREAD); - w.set(newPtr); - } + auto guard = makeGuard([&] { delete newPtr; }); + threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_); + + w.dispose(TLPDestructionMode::THIS_THREAD); + guard.dismiss(); + w.set(newPtr); } explicit operator bool() const { return get() != nullptr; } + /** + * reset() that transfers ownership from a smart pointer + */ + template < + typename SourceT, + typename Deleter, + typename = typename std::enable_if< + std::is_convertible::value>::type> + void reset(std::unique_ptr source) { + auto deleter = [delegate = source.get_deleter()]( + T * ptr, TLPDestructionMode) { + delegate(ptr); + }; + reset(source.release(), deleter); + } + + /** + * reset() that transfers ownership from a smart pointer with the default + * deleter + */ + template < + typename SourceT, + typename = typename std::enable_if< + std::is_convertible::value>::type> + void reset(std::unique_ptr source) { + reset(source.release()); + } + /** * reset() with a custom deleter: * deleter(T* ptr, TLPDestructionMode mode) * "mode" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus * deleting pointers for all threads), and THIS_THREAD if we're only deleting - * the member for one thread (because of thread exit or reset()) + * the member for one thread (because of thread exit or reset()). + * Invoking the deleter must not throw. */ template - void reset(T* newPtr, Deleter deleter) { - threadlocal_detail::ElementWrapper& w = - threadlocal_detail::StaticMeta::get(id_); - if (w.ptr != newPtr) { - w.dispose(TLPDestructionMode::THIS_THREAD); - w.set(newPtr, deleter); - } + void reset(T* newPtr, const Deleter& deleter) { + auto guard = makeGuard([&] { + if (newPtr) { + deleter(newPtr, TLPDestructionMode::THIS_THREAD); + } + }); + threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_); + w.dispose(TLPDestructionMode::THIS_THREAD); + guard.dismiss(); + w.set(newPtr, deleter); } // Holds a global lock for iteration through all thread local child objects. // Can be used as an iterable container. // Use accessAllThreads() to obtain one. class Accessor { - friend class ThreadLocalPtr; + friend class ThreadLocalPtr; - threadlocal_detail::StaticMeta& meta_; - boost::mutex* lock_; - int id_; + threadlocal_detail::StaticMetaBase& meta_; + SharedMutex* accessAllThreadsLock_; + std::mutex* lock_; + uint32_t id_; public: class Iterator; @@ -219,7 +272,7 @@ class ThreadLocalPtr { boost::bidirectional_traversal_tag> { // traversal friend class Accessor; friend class boost::iterator_core_access; - const Accessor* const accessor_; + const Accessor* accessor_; threadlocal_detail::ThreadEntry* e_; void increment() { @@ -276,15 +329,17 @@ class ThreadLocalPtr { Accessor(const Accessor&) = delete; Accessor& operator=(const Accessor&) = delete; - Accessor(Accessor&& other) FOLLY_NOEXCEPT - : meta_(other.meta_), - lock_(other.lock_), - id_(other.id_) { + Accessor(Accessor&& other) noexcept + : meta_(other.meta_), + accessAllThreadsLock_(other.accessAllThreadsLock_), + lock_(other.lock_), + id_(other.id_) { other.id_ = 0; + other.accessAllThreadsLock_ = nullptr; other.lock_ = nullptr; } - Accessor& operator=(Accessor&& other) FOLLY_NOEXCEPT { + Accessor& operator=(Accessor&& other) noexcept { // Each Tag has its own unique meta, and accessors with different Tags // have different types. So either *this is empty, or this and other // have the same tag. But if they have the same tag, they have the same @@ -294,20 +349,23 @@ class ThreadLocalPtr { assert(&meta_ == &other.meta_); assert(lock_ == nullptr); using std::swap; + swap(accessAllThreadsLock_, other.accessAllThreadsLock_); swap(lock_, other.lock_); swap(id_, other.id_); } Accessor() - : meta_(threadlocal_detail::StaticMeta::instance()), - lock_(nullptr), - id_(0) { - } + : meta_(threadlocal_detail::StaticMeta::instance()), + accessAllThreadsLock_(nullptr), + lock_(nullptr), + id_(0) {} private: - explicit Accessor(int id) - : meta_(threadlocal_detail::StaticMeta::instance()), - lock_(&meta_.lock_) { + explicit Accessor(uint32_t id) + : meta_(threadlocal_detail::StaticMeta::instance()), + accessAllThreadsLock_(&meta_.accessAllThreadsLock_), + lock_(&meta_.lock_) { + accessAllThreadsLock_->lock(); lock_->lock(); id_ = id; } @@ -315,8 +373,11 @@ class ThreadLocalPtr { void release() { if (lock_) { lock_->unlock(); + DCHECK(accessAllThreadsLock_ != nullptr); + accessAllThreadsLock_->unlock(); id_ = 0; lock_ = nullptr; + accessAllThreadsLock_ = nullptr; } } }; @@ -324,27 +385,21 @@ class ThreadLocalPtr { // accessor allows a client to iterate through all thread local child // elements of this ThreadLocal instance. Holds a global lock for each Accessor accessAllThreads() const { - FOLLY_ASSERT(static_assert(!std::is_same::value, - "Must use a unique Tag to use the accessAllThreads feature")); - return Accessor(id_); + static_assert(!std::is_same::value, + "Must use a unique Tag to use the accessAllThreads feature"); + return Accessor(id_.getOrAllocate(StaticMeta::instance())); } private: void destroy() { - if (id_) { - threadlocal_detail::StaticMeta::destroy(id_); - } + StaticMeta::instance().destroy(&id_); } // non-copyable ThreadLocalPtr(const ThreadLocalPtr&) = delete; ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete; - int id_; // every instantiation has a unique id + mutable typename StaticMeta::EntryID id_; }; -#undef FOLLY_NOEXCEPT - } // namespace folly - -#endif /* FOLLY_THREADLOCAL_H_ */