| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * Improved thread local storage for non-trivial types (similar speed as |
| * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster |
| * than boost::thread_specific_ptr). |
| * |
| * Also includes an accessor interface to walk all the thread local child |
| * 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. |
| * |
| * Intended use is for frequent write, infrequent read data access patterns such |
| * as counters. |
| * |
| * There are two classes here - ThreadLocal and ThreadLocalPtr. ThreadLocalPtr |
| * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin |
| * wrapper around ThreadLocalPtr that manages allocation automatically. |
| * |
| * @author Spencer Ahrens (sahrens) |
| */ |
| |
| #ifndef FOLLY_THREADLOCAL_H_ |
| #define FOLLY_THREADLOCAL_H_ |
| |
| #include <folly/Portability.h> |
| #include <boost/iterator/iterator_facade.hpp> |
| #include <folly/Likely.h> |
| #include <type_traits> |
| |
| |
| namespace folly { |
| enum class TLPDestructionMode { |
| THIS_THREAD, |
| ALL_THREADS |
| }; |
| } // namespace |
| |
| #include <folly/detail/ThreadLocalDetail.h> |
| |
| namespace folly { |
| |
| template<class T, class Tag> class ThreadLocalPtr; |
| |
| template<class T, class Tag=void> |
| class ThreadLocal { |
| public: |
| ThreadLocal() = default; |
| |
| T* get() const { |
| T* ptr = tlp_.get(); |
| if (LIKELY(ptr != nullptr)) { |
| return ptr; |
| } |
| |
| // separated new item creation out to speed up the fast path. |
| return makeTlp(); |
| } |
| |
| T* operator->() const { |
| return get(); |
| } |
| |
| T& operator*() const { |
| return *get(); |
| } |
| |
| void reset(T* newPtr = nullptr) { |
| tlp_.reset(newPtr); |
| } |
| |
| typedef typename ThreadLocalPtr<T,Tag>::Accessor Accessor; |
| Accessor accessAllThreads() const { |
| return tlp_.accessAllThreads(); |
| } |
| |
| // movable |
| ThreadLocal(ThreadLocal&&) = default; |
| ThreadLocal& operator=(ThreadLocal&&) = default; |
| |
| private: |
| // non-copyable |
| ThreadLocal(const ThreadLocal&) = delete; |
| ThreadLocal& operator=(const ThreadLocal&) = delete; |
| |
| T* makeTlp() const { |
| T* ptr = new T(); |
| tlp_.reset(ptr); |
| return ptr; |
| } |
| |
| mutable ThreadLocalPtr<T,Tag> tlp_; |
| }; |
| |
| /* |
| * The idea here is that __thread is faster than pthread_getspecific, so we |
| * keep a __thread array of pointers to objects (ThreadEntry::elements) where |
| * each array has an index for each unique instance of the ThreadLocalPtr |
| * object. Each ThreadLocalPtr object has a unique id that is an index into |
| * these arrays so we can fetch the correct object from thread local storage |
| * very efficiently. |
| * |
| * In order to prevent unbounded growth of the id space and thus huge |
| * ThreadEntry::elements, arrays, for example due to continuous creation and |
| * destruction of ThreadLocalPtr objects, we keep a set of all active |
| * instances. When an instance is destroyed we remove it from the active |
| * set and insert the id into freeIds_ for reuse. These operations require a |
| * global mutex, but only happen at construction and destruction time. |
| * |
| * 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<class T, class Tag=void> |
| class ThreadLocalPtr { |
| public: |
| ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta<Tag>::create()) { } |
| |
| ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : id_(other.id_) { |
| other.id_ = 0; |
| } |
| |
| ThreadLocalPtr& operator=(ThreadLocalPtr&& other) { |
| assert(this != &other); |
| destroy(); |
| id_ = other.id_; |
| other.id_ = 0; |
| return *this; |
| } |
| |
| ~ThreadLocalPtr() { |
| destroy(); |
| } |
| |
| T* get() const { |
| return static_cast<T*>(threadlocal_detail::StaticMeta<Tag>::get(id_).ptr); |
| } |
| |
| T* operator->() const { |
| return get(); |
| } |
| |
| T& operator*() const { |
| return *get(); |
| } |
| |
| T* release() { |
| threadlocal_detail::ElementWrapper& w = |
| threadlocal_detail::StaticMeta<Tag>::get(id_); |
| |
| return static_cast<T*>(w.release()); |
| } |
| |
| void reset(T* newPtr = nullptr) { |
| threadlocal_detail::ElementWrapper& w = |
| threadlocal_detail::StaticMeta<Tag>::get(id_); |
| if (w.ptr != newPtr) { |
| w.dispose(TLPDestructionMode::THIS_THREAD); |
| w.set(newPtr); |
| } |
| } |
| |
| explicit operator bool() const { |
| return get() != nullptr; |
| } |
| |
| /** |
| * 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()) |
| */ |
| template <class Deleter> |
| void reset(T* newPtr, Deleter deleter) { |
| threadlocal_detail::ElementWrapper& w = |
| threadlocal_detail::StaticMeta<Tag>::get(id_); |
| if (w.ptr != newPtr) { |
| w.dispose(TLPDestructionMode::THIS_THREAD); |
| 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<T,Tag>; |
| |
| threadlocal_detail::StaticMeta<Tag>& meta_; |
| std::mutex* lock_; |
| uint32_t id_; |
| |
| public: |
| class Iterator; |
| friend class Iterator; |
| |
| // The iterators obtained from Accessor are bidirectional iterators. |
| class Iterator : public boost::iterator_facade< |
| Iterator, // Derived |
| T, // value_type |
| boost::bidirectional_traversal_tag> { // traversal |
| friend class Accessor; |
| friend class boost::iterator_core_access; |
| const Accessor* const accessor_; |
| threadlocal_detail::ThreadEntry* e_; |
| |
| void increment() { |
| e_ = e_->next; |
| incrementToValid(); |
| } |
| |
| void decrement() { |
| e_ = e_->prev; |
| decrementToValid(); |
| } |
| |
| T& dereference() const { |
| return *static_cast<T*>(e_->elements[accessor_->id_].ptr); |
| } |
| |
| bool equal(const Iterator& other) const { |
| return (accessor_->id_ == other.accessor_->id_ && |
| e_ == other.e_); |
| } |
| |
| explicit Iterator(const Accessor* accessor) |
| : accessor_(accessor), |
| e_(&accessor_->meta_.head_) { |
| } |
| |
| bool valid() const { |
| return (e_->elements && |
| accessor_->id_ < e_->elementsCapacity && |
| e_->elements[accessor_->id_].ptr); |
| } |
| |
| void incrementToValid() { |
| for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->next) { } |
| } |
| |
| void decrementToValid() { |
| for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->prev) { } |
| } |
| }; |
| |
| ~Accessor() { |
| release(); |
| } |
| |
| Iterator begin() const { |
| return ++Iterator(this); |
| } |
| |
| Iterator end() const { |
| return Iterator(this); |
| } |
| |
| Accessor(const Accessor&) = delete; |
| Accessor& operator=(const Accessor&) = delete; |
| |
| Accessor(Accessor&& other) noexcept |
| : meta_(other.meta_), |
| lock_(other.lock_), |
| id_(other.id_) { |
| other.id_ = 0; |
| other.lock_ = nullptr; |
| } |
| |
| 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 |
| // meta (and lock), so they'd both hold the lock at the same time, |
| // which is impossible, which leaves only one possible scenario -- |
| // *this is empty. Assert it. |
| assert(&meta_ == &other.meta_); |
| assert(lock_ == nullptr); |
| using std::swap; |
| swap(lock_, other.lock_); |
| swap(id_, other.id_); |
| } |
| |
| Accessor() |
| : meta_(threadlocal_detail::StaticMeta<Tag>::instance()), |
| lock_(nullptr), |
| id_(0) { |
| } |
| |
| private: |
| explicit Accessor(uint32_t id) |
| : meta_(threadlocal_detail::StaticMeta<Tag>::instance()), |
| lock_(&meta_.lock_) { |
| lock_->lock(); |
| id_ = id; |
| } |
| |
| void release() { |
| if (lock_) { |
| lock_->unlock(); |
| id_ = 0; |
| lock_ = nullptr; |
| } |
| } |
| }; |
| |
| // accessor allows a client to iterate through all thread local child |
| // elements of this ThreadLocal instance. Holds a global lock for each <Tag> |
| Accessor accessAllThreads() const { |
| static_assert(!std::is_same<Tag, void>::value, |
| "Must use a unique Tag to use the accessAllThreads feature"); |
| return Accessor(id_); |
| } |
| |
| private: |
| void destroy() { |
| if (id_) { |
| threadlocal_detail::StaticMeta<Tag>::destroy(id_); |
| } |
| } |
| |
| // non-copyable |
| ThreadLocalPtr(const ThreadLocalPtr&) = delete; |
| ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete; |
| |
| uint32_t id_; // every instantiation has a unique id |
| }; |
| |
| } // namespace folly |
| |
| #endif /* FOLLY_THREADLOCAL_H_ */ |