proxygen
ThreadLocal.h
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
41 #pragma once
42 
43 #include <iterator>
44 #include <type_traits>
45 #include <utility>
46 
47 #include <folly/Likely.h>
48 #include <folly/Portability.h>
49 #include <folly/ScopeGuard.h>
50 #include <folly/SharedMutex.h>
52 
53 namespace folly {
54 
55 template <class T, class Tag, class AccessMode>
57 
58 template <class T, class Tag = void, class AccessMode = void>
59 class ThreadLocal {
60  public:
61  constexpr ThreadLocal() : constructor_([]() { return new T(); }) {}
62 
63  template <
64  typename F,
66  explicit ThreadLocal(F&& constructor)
67  : constructor_(std::forward<F>(constructor)) {}
68 
70  auto const ptr = tlp_.get();
71  return FOLLY_LIKELY(!!ptr) ? ptr : makeTlp();
72  }
73 
74  T* operator->() const {
75  return get();
76  }
77 
78  T& operator*() const {
79  return *get();
80  }
81 
82  void reset(T* newPtr = nullptr) {
83  tlp_.reset(newPtr);
84  }
85 
87  Accessor accessAllThreads() const {
88  return tlp_.accessAllThreads();
89  }
90 
91  // movable
92  ThreadLocal(ThreadLocal&&) = default;
93  ThreadLocal& operator=(ThreadLocal&&) = default;
94 
95  private:
96  // non-copyable
97  ThreadLocal(const ThreadLocal&) = delete;
98  ThreadLocal& operator=(const ThreadLocal&) = delete;
99 
101  auto const ptr = constructor_();
102  tlp_.reset(ptr);
103  return ptr;
104  }
105 
107  std::function<T*()> constructor_;
108 };
109 
110 /*
111  * The idea here is that __thread is faster than pthread_getspecific, so we
112  * keep a __thread array of pointers to objects (ThreadEntry::elements) where
113  * each array has an index for each unique instance of the ThreadLocalPtr
114  * object. Each ThreadLocalPtr object has a unique id that is an index into
115  * these arrays so we can fetch the correct object from thread local storage
116  * very efficiently.
117  *
118  * In order to prevent unbounded growth of the id space and thus huge
119  * ThreadEntry::elements, arrays, for example due to continuous creation and
120  * destruction of ThreadLocalPtr objects, we keep a set of all active
121  * instances. When an instance is destroyed we remove it from the active
122  * set and insert the id into freeIds_ for reuse. These operations require a
123  * global mutex, but only happen at construction and destruction time.
124  *
125  * We use a single global pthread_key_t per Tag to manage object destruction and
126  * memory cleanup upon thread exit because there is a finite number of
127  * pthread_key_t's available per machine.
128  *
129  * NOTE: Apple platforms don't support the same semantics for __thread that
130  * Linux does (and it's only supported at all on i386). For these, use
131  * pthread_setspecific()/pthread_getspecific() for the per-thread
132  * storage. Windows (MSVC and GCC) does support the same semantics
133  * with __declspec(thread)
134  */
135 
136 template <class T, class Tag = void, class AccessMode = void>
137 class ThreadLocalPtr {
138  private:
140 
141  public:
142  constexpr ThreadLocalPtr() : id_() {}
143 
144  ThreadLocalPtr(ThreadLocalPtr&& other) noexcept : id_(std::move(other.id_)) {}
145 
147  assert(this != &other);
148  destroy();
149  id_ = std::move(other.id_);
150  return *this;
151  }
152 
154  destroy();
155  }
156 
157  T* get() const {
159  return static_cast<T*>(w.ptr);
160  }
161 
162  T* operator->() const {
163  return get();
164  }
165 
166  T& operator*() const {
167  return *get();
168  }
169 
170  T* release() {
172 
173  return static_cast<T*>(w.release());
174  }
175 
176  void reset(T* newPtr = nullptr) {
177  auto guard = makeGuard([&] { delete newPtr; });
179 
181  // need to get a new ptr since the
182  // ThreadEntry::elements array can be reallocated
183  w = &StaticMeta::get(&id_);
184  w->cleanup();
185  guard.dismiss();
186  w->set(newPtr);
187  }
188 
189  explicit operator bool() const {
190  return get() != nullptr;
191  }
192 
196  template <
197  typename SourceT,
198  typename Deleter,
199  typename = typename std::enable_if<
201  void reset(std::unique_ptr<SourceT, Deleter> source) {
202  auto deleter = [delegate = source.get_deleter()](
203  T* ptr, TLPDestructionMode) { delegate(ptr); };
204  reset(source.release(), deleter);
205  }
206 
211  template <
212  typename SourceT,
213  typename = typename std::enable_if<
215  void reset(std::unique_ptr<SourceT> source) {
216  reset(source.release());
217  }
218 
227  template <class Deleter>
228  void reset(T* newPtr, const Deleter& deleter) {
229  auto guard = makeGuard([&] {
230  if (newPtr) {
231  deleter(newPtr, TLPDestructionMode::THIS_THREAD);
232  }
233  });
236  // need to get a new ptr since the
237  // ThreadEntry::elements array can be reallocated
238  w = &StaticMeta::get(&id_);
239  w->cleanup();
240  guard.dismiss();
241  w->set(newPtr, deleter);
242  }
243 
244  // Holds a global lock for iteration through all thread local child objects.
245  // Can be used as an iterable container.
246  // Use accessAllThreads() to obtain one.
247  class Accessor {
248  friend class ThreadLocalPtr<T, Tag, AccessMode>;
249 
254 
255  public:
256  class Iterator;
257  friend class Iterator;
258 
259  // The iterators obtained from Accessor are bidirectional iterators.
260  class Iterator {
261  friend class Accessor;
264 
265  void increment() {
266  e_ = e_->getNext();
267  incrementToValid();
268  }
269 
270  void decrement() {
271  e_ = e_->getPrev();
272  decrementToValid();
273  }
274 
275  const T& dereference() const {
276  return *static_cast<T*>(
277  e_->getThreadEntry()->elements[accessor_->id_].ptr);
278  }
279 
281  return *static_cast<T*>(
282  e_->getThreadEntry()->elements[accessor_->id_].ptr);
283  }
284 
285  bool equal(const Iterator& other) const {
286  return (accessor_->id_ == other.accessor_->id_ && e_ == other.e_);
287  }
288 
289  explicit Iterator(const Accessor* accessor)
290  : accessor_(accessor),
291  e_(&accessor_->meta_.head_.elements[accessor_->id_].node) {}
292 
293  // we just need to check the ptr since it can be set to nullptr
294  // even if the entry is part of the list
295  bool valid() const {
296  return (e_->getThreadEntry()->elements[accessor_->id_].ptr);
297  }
298 
300  for (; e_ != &accessor_->meta_.head_.elements[accessor_->id_].node &&
301  !valid();
302  e_ = e_->getNext()) {
303  }
304  }
305 
307  for (; e_ != &accessor_->meta_.head_.elements[accessor_->id_].node &&
308  !valid();
309  e_ = e_->getPrev()) {
310  }
311  }
312 
313  public:
314  using difference_type = ssize_t;
315  using value_type = T;
316  using reference = T const&;
317  using pointer = T const*;
318  using iterator_category = std::bidirectional_iterator_tag;
319 
321  increment();
322  return *this;
323  }
324 
326  Iterator copy(*this);
327  increment();
328  return copy;
329  }
330 
332  decrement();
333  return *this;
334  }
335 
337  Iterator copy(*this);
338  decrement();
339  return copy;
340  }
341 
343  return dereference();
344  }
345 
346  T const& operator*() const {
347  return dereference();
348  }
349 
351  return &dereference();
352  }
353 
354  T const* operator->() const {
355  return &dereference();
356  }
357 
358  bool operator==(Iterator const& rhs) const {
359  return equal(rhs);
360  }
361 
362  bool operator!=(Iterator const& rhs) const {
363  return !equal(rhs);
364  }
365  };
366 
368  release();
369  }
370 
371  Iterator begin() const {
372  return ++Iterator(this);
373  }
374 
375  Iterator end() const {
376  return Iterator(this);
377  }
378 
379  Accessor(const Accessor&) = delete;
380  Accessor& operator=(const Accessor&) = delete;
381 
383  : meta_(other.meta_),
384  accessAllThreadsLock_(other.accessAllThreadsLock_),
385  lock_(other.lock_),
386  id_(other.id_) {
387  other.id_ = 0;
388  other.accessAllThreadsLock_ = nullptr;
389  other.lock_ = nullptr;
390  }
391 
393  // Each Tag has its own unique meta, and accessors with different Tags
394  // have different types. So either *this is empty, or this and other
395  // have the same tag. But if they have the same tag, they have the same
396  // meta (and lock), so they'd both hold the lock at the same time,
397  // which is impossible, which leaves only one possible scenario --
398  // *this is empty. Assert it.
399  assert(&meta_ == &other.meta_);
400  assert(lock_ == nullptr);
401  using std::swap;
402  swap(accessAllThreadsLock_, other.accessAllThreadsLock_);
403  swap(lock_, other.lock_);
404  swap(id_, other.id_);
405  }
406 
408  : meta_(threadlocal_detail::StaticMeta<Tag, AccessMode>::instance()),
409  accessAllThreadsLock_(nullptr),
410  lock_(nullptr),
411  id_(0) {}
412 
413  private:
414  explicit Accessor(uint32_t id)
415  : meta_(threadlocal_detail::StaticMeta<Tag, AccessMode>::instance()),
416  accessAllThreadsLock_(&meta_.accessAllThreadsLock_),
417  lock_(&meta_.lock_) {
418  accessAllThreadsLock_->lock();
419  lock_->lock();
420  id_ = id;
421  }
422 
423  void release() {
424  if (lock_) {
425  lock_->unlock();
426  DCHECK(accessAllThreadsLock_ != nullptr);
427  accessAllThreadsLock_->unlock();
428  id_ = 0;
429  lock_ = nullptr;
430  accessAllThreadsLock_ = nullptr;
431  }
432  }
433  };
434 
435  // accessor allows a client to iterate through all thread local child
436  // elements of this ThreadLocal instance. Holds a global lock for each <Tag>
438  static_assert(
440  "Must use a unique Tag to use the accessAllThreads feature");
441  return Accessor(id_.getOrAllocate(StaticMeta::instance()));
442  }
443 
444  private:
445  void destroy() {
446  StaticMeta::instance().destroy(&id_);
447  }
448 
449  // non-copyable
450  ThreadLocalPtr(const ThreadLocalPtr&) = delete;
451  ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete;
452 
453  mutable typename StaticMeta::EntryID id_;
454 };
455 
456 namespace threadlocal_detail {
457 template <typename>
459 
460 template <typename T, typename Tag, typename AccessMode>
461 struct static_meta_of<ThreadLocalPtr<T, Tag, AccessMode>> {
463 };
464 
465 template <typename T, typename Tag, typename AccessMode>
466 struct static_meta_of<ThreadLocal<T, Tag, AccessMode>> {
468 };
469 
470 } // namespace threadlocal_detail
471 } // namespace folly
T & operator*() const
Definition: ThreadLocal.h:78
void * ptr
FOLLY_ALWAYS_INLINE ThreadEntry * getThreadEntry()
std::function< T *()> constructor_
Definition: ThreadLocal.h:107
bool operator==(Iterator const &rhs) const
Definition: ThreadLocal.h:358
constexpr ThreadLocal()
Definition: ThreadLocal.h:61
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:176
void reset(T *newPtr=nullptr)
Definition: ThreadLocal.h:82
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
FOLLY_NOINLINE T * makeTlp() const
Definition: ThreadLocal.h:100
PskType type
ThreadLocal & operator=(ThreadLocal &&)=default
Accessor accessAllThreads() const
Definition: ThreadLocal.h:87
bool operator!=(Iterator const &rhs) const
Definition: ThreadLocal.h:362
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
Accessor(Accessor &&other) noexcept
Definition: ThreadLocal.h:382
STL namespace.
T * operator->() const
Definition: ThreadLocal.h:74
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
ThreadLocalPtr< T, Tag, AccessMode > tlp_
Definition: ThreadLocal.h:106
#define nullptr
Definition: http_parser.c:41
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
static void destroy()
ThreadLocalPtr & operator=(ThreadLocalPtr &&other)
Definition: ThreadLocal.h:146
StaticMeta::EntryID id_
Definition: ThreadLocal.h:453
constexpr std::decay< T >::type copy(T &&value) noexcept(noexcept(typename std::decay< T >::type(std::forward< T >(value))))
Definition: Utility.h:72
#define FOLLY_NOINLINE
Definition: CPortability.h:142
std::bidirectional_iterator_tag iterator_category
Definition: ThreadLocal.h:318
typename T::type _t
Definition: Traits.h:171
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
bool equal(const Iterator &other) const
Definition: ThreadLocal.h:285
Accessor & operator=(Accessor &&other) noexcept
Definition: ThreadLocal.h:392
void reset(T *newPtr, const Deleter &deleter)
Definition: ThreadLocal.h:228
Accessor accessAllThreads() const
Definition: ThreadLocal.h:437
static const char *const value
Definition: Conv.cpp:50
FOLLY_ALWAYS_INLINE ThreadEntryNode * getNext()
void swap(exception_wrapper &a, exception_wrapper &b) noexcept
#define FOLLY_LIKELY(x)
Definition: Likely.h:35
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
void reset(std::unique_ptr< SourceT > source)
Definition: ThreadLocal.h:215
T * operator->() const
Definition: ThreadLocal.h:162
std::mutex mutex
void reset(std::unique_ptr< SourceT, Deleter > source)
Definition: ThreadLocal.h:201
ThreadLocal(F &&constructor)
Definition: ThreadLocal.h:66
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
Iterator(const Accessor *accessor)
Definition: ThreadLocal.h:289
ThreadLocalPtr(ThreadLocalPtr &&other) noexcept
Definition: ThreadLocal.h:144
T & operator*() const
Definition: ThreadLocal.h:166
#define FOLLY_ATTR_VISIBILITY_HIDDEN
Definition: CPortability.h:160
PUSHMI_INLINE_VAR constexpr detail::get_fn< T > get
Definition: submit.h:391
threadlocal_detail::ThreadEntryNode * e_
Definition: ThreadLocal.h:263
ThreadLocalPtr< T, Tag, AccessMode >::Accessor Accessor
Definition: ThreadLocal.h:86
FOLLY_ALWAYS_INLINE ThreadEntryNode * getPrev()
threadlocal_detail::StaticMetaBase & meta_
Definition: ThreadLocal.h:250
constexpr ThreadLocalPtr()
Definition: ThreadLocal.h:142
threadlocal_detail::StaticMeta< Tag, AccessMode > StaticMeta
Definition: ThreadLocal.h:139
constexpr detail::Dereference dereference
Definition: Base-inl.h:2575