Fix ThreadCachedInt race condition
[folly.git] / folly / ThreadLocal.h
1 /*
2  * Copyright 2016 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
17 /**
18  * Improved thread local storage for non-trivial types (similar speed as
19  * pthread_getspecific but only consumes a single pthread_key_t, and 4x faster
20  * than boost::thread_specific_ptr).
21  *
22  * Also includes an accessor interface to walk all the thread local child
23  * objects of a parent.  accessAllThreads() initializes an accessor which holds
24  * a global lock *that blocks all creation and destruction of ThreadLocal
25  * objects with the same Tag* and can be used as an iterable container.
26  *
27  * Intended use is for frequent write, infrequent read data access patterns such
28  * as counters.
29  *
30  * There are two classes here - ThreadLocal and ThreadLocalPtr.  ThreadLocalPtr
31  * has semantics similar to boost::thread_specific_ptr. ThreadLocal is a thin
32  * wrapper around ThreadLocalPtr that manages allocation automatically.
33  *
34  * @author Spencer Ahrens (sahrens)
35  */
36
37 #pragma once
38
39 #include <boost/iterator/iterator_facade.hpp>
40 #include <folly/Likely.h>
41 #include <folly/Portability.h>
42 #include <folly/ScopeGuard.h>
43 #include <folly/SharedMutex.h>
44 #include <type_traits>
45 #include <utility>
46
47 namespace folly {
48 enum class TLPDestructionMode {
49   THIS_THREAD,
50   ALL_THREADS
51 };
52 }  // namespace
53
54 #include <folly/detail/ThreadLocalDetail.h>
55
56 namespace folly {
57
58 template<class T, class Tag> class ThreadLocalPtr;
59
60 template<class T, class Tag=void>
61 class ThreadLocal {
62  public:
63   constexpr ThreadLocal() : constructor_([]() {
64       return new T();
65     }) {}
66
67   explicit ThreadLocal(std::function<T*()> constructor) :
68       constructor_(constructor) {
69   }
70
71   T* get() const {
72     T* ptr = tlp_.get();
73     if (LIKELY(ptr != nullptr)) {
74       return ptr;
75     }
76
77     // separated new item creation out to speed up the fast path.
78     return makeTlp();
79   }
80
81   T* operator->() const {
82     return get();
83   }
84
85   T& operator*() const {
86     return *get();
87   }
88
89   void reset(T* newPtr = nullptr) {
90     tlp_.reset(newPtr);
91   }
92
93   typedef typename ThreadLocalPtr<T,Tag>::Accessor Accessor;
94   Accessor accessAllThreads() const {
95     return tlp_.accessAllThreads();
96   }
97
98   // movable
99   ThreadLocal(ThreadLocal&&) = default;
100   ThreadLocal& operator=(ThreadLocal&&) = default;
101
102  private:
103   // non-copyable
104   ThreadLocal(const ThreadLocal&) = delete;
105   ThreadLocal& operator=(const ThreadLocal&) = delete;
106
107   T* makeTlp() const {
108     auto ptr = constructor_();
109     tlp_.reset(ptr);
110     return ptr;
111   }
112
113   mutable ThreadLocalPtr<T,Tag> tlp_;
114   std::function<T*()> constructor_;
115 };
116
117 /*
118  * The idea here is that __thread is faster than pthread_getspecific, so we
119  * keep a __thread array of pointers to objects (ThreadEntry::elements) where
120  * each array has an index for each unique instance of the ThreadLocalPtr
121  * object.  Each ThreadLocalPtr object has a unique id that is an index into
122  * these arrays so we can fetch the correct object from thread local storage
123  * very efficiently.
124  *
125  * In order to prevent unbounded growth of the id space and thus huge
126  * ThreadEntry::elements, arrays, for example due to continuous creation and
127  * destruction of ThreadLocalPtr objects, we keep a set of all active
128  * instances.  When an instance is destroyed we remove it from the active
129  * set and insert the id into freeIds_ for reuse.  These operations require a
130  * global mutex, but only happen at construction and destruction time.
131  *
132  * We use a single global pthread_key_t per Tag to manage object destruction and
133  * memory cleanup upon thread exit because there is a finite number of
134  * pthread_key_t's available per machine.
135  *
136  * NOTE: Apple platforms don't support the same semantics for __thread that
137  *       Linux does (and it's only supported at all on i386). For these, use
138  *       pthread_setspecific()/pthread_getspecific() for the per-thread
139  *       storage.  Windows (MSVC and GCC) does support the same semantics
140  *       with __declspec(thread)
141  */
142
143 template<class T, class Tag=void>
144 class ThreadLocalPtr {
145  private:
146   typedef threadlocal_detail::StaticMeta<Tag> StaticMeta;
147  public:
148   constexpr ThreadLocalPtr() : id_() {}
149
150   ThreadLocalPtr(ThreadLocalPtr&& other) noexcept :
151     id_(std::move(other.id_)) {
152   }
153
154   ThreadLocalPtr& operator=(ThreadLocalPtr&& other) {
155     assert(this != &other);
156     destroy();
157     id_ = std::move(other.id_);
158     return *this;
159   }
160
161   ~ThreadLocalPtr() {
162     destroy();
163   }
164
165   T* get() const {
166     threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_);
167     return static_cast<T*>(w.ptr);
168   }
169
170   T* operator->() const {
171     return get();
172   }
173
174   T& operator*() const {
175     return *get();
176   }
177
178   T* release() {
179     threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_);
180
181     return static_cast<T*>(w.release());
182   }
183
184   void reset(T* newPtr = nullptr) {
185     auto guard = makeGuard([&] { delete newPtr; });
186     threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_);
187
188     w.dispose(TLPDestructionMode::THIS_THREAD);
189     guard.dismiss();
190     w.set(newPtr);
191   }
192
193   explicit operator bool() const {
194     return get() != nullptr;
195   }
196
197   /**
198    * reset() that transfers ownership from a smart pointer
199    */
200   template <
201       typename SourceT,
202       typename Deleter,
203       typename = typename std::enable_if<
204           std::is_convertible<SourceT*, T*>::value>::type>
205   void reset(std::unique_ptr<SourceT, Deleter> source) {
206     auto deleter = [delegate = source.get_deleter()](
207         T * ptr, TLPDestructionMode) {
208       delegate(ptr);
209     };
210     reset(source.release(), deleter);
211   }
212
213   /**
214    * reset() that transfers ownership from a smart pointer with the default
215    * deleter
216    */
217   template <
218       typename SourceT,
219       typename = typename std::enable_if<
220           std::is_convertible<SourceT*, T*>::value>::type>
221   void reset(std::unique_ptr<SourceT> source) {
222     reset(source.release());
223   }
224
225   /**
226    * reset() with a custom deleter:
227    * deleter(T* ptr, TLPDestructionMode mode)
228    * "mode" is ALL_THREADS if we're destructing this ThreadLocalPtr (and thus
229    * deleting pointers for all threads), and THIS_THREAD if we're only deleting
230    * the member for one thread (because of thread exit or reset()).
231    * Invoking the deleter must not throw.
232    */
233   template <class Deleter>
234   void reset(T* newPtr, const Deleter& deleter) {
235     auto guard = makeGuard([&] {
236       if (newPtr) {
237         deleter(newPtr, TLPDestructionMode::THIS_THREAD);
238       }
239     });
240     threadlocal_detail::ElementWrapper& w = StaticMeta::instance().get(&id_);
241     w.dispose(TLPDestructionMode::THIS_THREAD);
242     guard.dismiss();
243     w.set(newPtr, deleter);
244   }
245
246   // Holds a global lock for iteration through all thread local child objects.
247   // Can be used as an iterable container.
248   // Use accessAllThreads() to obtain one.
249   class Accessor {
250     friend class ThreadLocalPtr<T,Tag>;
251
252     threadlocal_detail::StaticMetaBase& meta_;
253     SharedMutex* accessAllThreadsLock_;
254     std::mutex* lock_;
255     uint32_t id_;
256
257    public:
258     class Iterator;
259     friend class Iterator;
260
261     // The iterators obtained from Accessor are bidirectional iterators.
262     class Iterator : public boost::iterator_facade<
263           Iterator,                               // Derived
264           T,                                      // value_type
265           boost::bidirectional_traversal_tag> {   // traversal
266       friend class Accessor;
267       friend class boost::iterator_core_access;
268       const Accessor* accessor_;
269       threadlocal_detail::ThreadEntry* e_;
270
271       void increment() {
272         e_ = e_->next;
273         incrementToValid();
274       }
275
276       void decrement() {
277         e_ = e_->prev;
278         decrementToValid();
279       }
280
281       T& dereference() const {
282         return *static_cast<T*>(e_->elements[accessor_->id_].ptr);
283       }
284
285       bool equal(const Iterator& other) const {
286         return (accessor_->id_ == other.accessor_->id_ &&
287                 e_ == other.e_);
288       }
289
290       explicit Iterator(const Accessor* accessor)
291         : accessor_(accessor),
292           e_(&accessor_->meta_.head_) {
293       }
294
295       bool valid() const {
296         return (e_->elements &&
297                 accessor_->id_ < e_->elementsCapacity &&
298                 e_->elements[accessor_->id_].ptr);
299       }
300
301       void incrementToValid() {
302         for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->next) { }
303       }
304
305       void decrementToValid() {
306         for (; e_ != &accessor_->meta_.head_ && !valid(); e_ = e_->prev) { }
307       }
308     };
309
310     ~Accessor() {
311       release();
312     }
313
314     Iterator begin() const {
315       return ++Iterator(this);
316     }
317
318     Iterator end() const {
319       return Iterator(this);
320     }
321
322     Accessor(const Accessor&) = delete;
323     Accessor& operator=(const Accessor&) = delete;
324
325     Accessor(Accessor&& other) noexcept
326         : meta_(other.meta_),
327           accessAllThreadsLock_(other.accessAllThreadsLock_),
328           lock_(other.lock_),
329           id_(other.id_) {
330       other.id_ = 0;
331       other.accessAllThreadsLock_ = nullptr;
332       other.lock_ = nullptr;
333     }
334
335     Accessor& operator=(Accessor&& other) noexcept {
336       // Each Tag has its own unique meta, and accessors with different Tags
337       // have different types.  So either *this is empty, or this and other
338       // have the same tag.  But if they have the same tag, they have the same
339       // meta (and lock), so they'd both hold the lock at the same time,
340       // which is impossible, which leaves only one possible scenario --
341       // *this is empty.  Assert it.
342       assert(&meta_ == &other.meta_);
343       assert(lock_ == nullptr);
344       using std::swap;
345       swap(accessAllThreadsLock_, other.accessAllThreadsLock_);
346       swap(lock_, other.lock_);
347       swap(id_, other.id_);
348     }
349
350     Accessor()
351         : meta_(threadlocal_detail::StaticMeta<Tag>::instance()),
352           accessAllThreadsLock_(nullptr),
353           lock_(nullptr),
354           id_(0) {}
355
356    private:
357     explicit Accessor(uint32_t id)
358         : meta_(threadlocal_detail::StaticMeta<Tag>::instance()),
359           accessAllThreadsLock_(&meta_.accessAllThreadsLock_),
360           lock_(&meta_.lock_) {
361       accessAllThreadsLock_->lock();
362       lock_->lock();
363       id_ = id;
364     }
365
366     void release() {
367       if (lock_) {
368         lock_->unlock();
369         DCHECK(accessAllThreadsLock_ != nullptr);
370         accessAllThreadsLock_->unlock();
371         id_ = 0;
372         lock_ = nullptr;
373         accessAllThreadsLock_ = nullptr;
374       }
375     }
376   };
377
378   // accessor allows a client to iterate through all thread local child
379   // elements of this ThreadLocal instance.  Holds a global lock for each <Tag>
380   Accessor accessAllThreads() const {
381     static_assert(!std::is_same<Tag, void>::value,
382                   "Must use a unique Tag to use the accessAllThreads feature");
383     return Accessor(id_.getOrAllocate(StaticMeta::instance()));
384   }
385
386  private:
387   void destroy() {
388     StaticMeta::instance().destroy(&id_);
389   }
390
391   // non-copyable
392   ThreadLocalPtr(const ThreadLocalPtr&) = delete;
393   ThreadLocalPtr& operator=(const ThreadLocalPtr&) = delete;
394
395   mutable typename StaticMeta::EntryID id_;
396 };
397
398 }  // namespace folly