X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FThreadLocal.h;h=3565c210a25e0279bd6d1ce614cf684a8fe3852e;hp=fee6fac4b0ce4e4524dafa20f3bcb58c34af7b30;hb=96791c4516497b4ea2ad08af12e4267bd1c4e796;hpb=0efcd8c8283cdfc57d83594637dcb8b5fec09288 diff --git a/folly/ThreadLocal.h b/folly/ThreadLocal.h index fee6fac4..3565c210 100644 --- a/folly/ThreadLocal.h +++ b/folly/ThreadLocal.h @@ -1,5 +1,5 @@ /* - * Copyright 2015 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,32 +38,41 @@ * @author Spencer Ahrens (sahrens) */ -#ifndef FOLLY_THREADLOCAL_H_ -#define FOLLY_THREADLOCAL_H_ +#pragma once -#include #include #include +#include +#include +#include #include - +#include namespace folly { enum class TLPDestructionMode { THIS_THREAD, ALL_THREADS }; +struct AccessModeStrict {}; } // namespace #include namespace folly { -template class ThreadLocalPtr; +template +class ThreadLocalPtr; -template +template class ThreadLocal { public: - ThreadLocal() = default; + constexpr ThreadLocal() : constructor_([]() { + return new T(); + }) {} + + explicit ThreadLocal(std::function constructor) : + constructor_(constructor) { + } T* get() const { T* ptr = tlp_.get(); @@ -83,7 +96,7 @@ class ThreadLocal { tlp_.reset(newPtr); } - typedef typename ThreadLocalPtr::Accessor Accessor; + typedef typename ThreadLocalPtr::Accessor Accessor; Accessor accessAllThreads() const { return tlp_.accessAllThreads(); } @@ -98,12 +111,13 @@ class ThreadLocal { ThreadLocal& operator=(const ThreadLocal&) = delete; T* makeTlp() const { - T* ptr = new T(); + auto ptr = constructor_(); tlp_.reset(ptr); return ptr; } - mutable ThreadLocalPtr tlp_; + mutable ThreadLocalPtr tlp_; + std::function constructor_; }; /* @@ -132,20 +146,22 @@ class ThreadLocal { * 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) noexcept : 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; } @@ -154,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 { @@ -166,49 +183,81 @@ class ThreadLocalPtr { } T* release() { - threadlocal_detail::ElementWrapper& w = - threadlocal_detail::StaticMeta::get(id_); + 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_; + threadlocal_detail::StaticMetaBase& meta_; + SharedMutex* accessAllThreadsLock_; std::mutex* lock_; uint32_t id_; @@ -223,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() { @@ -281,10 +330,12 @@ class ThreadLocalPtr { Accessor& operator=(const Accessor&) = delete; Accessor(Accessor&& other) noexcept - : meta_(other.meta_), - lock_(other.lock_), - id_(other.id_) { + : meta_(other.meta_), + accessAllThreadsLock_(other.accessAllThreadsLock_), + lock_(other.lock_), + id_(other.id_) { other.id_ = 0; + other.accessAllThreadsLock_ = nullptr; other.lock_ = nullptr; } @@ -298,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(uint32_t id) - : meta_(threadlocal_detail::StaticMeta::instance()), - lock_(&meta_.lock_) { + : meta_(threadlocal_detail::StaticMeta::instance()), + accessAllThreadsLock_(&meta_.accessAllThreadsLock_), + lock_(&meta_.lock_) { + accessAllThreadsLock_->lock(); lock_->lock(); id_ = id; } @@ -319,8 +373,11 @@ class ThreadLocalPtr { void release() { if (lock_) { lock_->unlock(); + DCHECK(accessAllThreadsLock_ != nullptr); + accessAllThreadsLock_->unlock(); id_ = 0; lock_ = nullptr; + accessAllThreadsLock_ = nullptr; } } }; @@ -330,23 +387,19 @@ class ThreadLocalPtr { Accessor accessAllThreads() const { static_assert(!std::is_same::value, "Must use a unique Tag to use the accessAllThreads feature"); - return Accessor(id_); + 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; - uint32_t id_; // every instantiation has a unique id + mutable typename StaticMeta::EntryID id_; }; } // namespace folly - -#endif /* FOLLY_THREADLOCAL_H_ */