Fix bad bug in folly::ThreadLocal
[folly.git] / folly / detail / ThreadLocalDetail.h
1 /*
2  * Copyright 2013 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 #ifndef FOLLY_DETAIL_THREADLOCALDETAIL_H_
18 #define FOLLY_DETAIL_THREADLOCALDETAIL_H_
19
20 #include <limits.h>
21 #include <pthread.h>
22
23 #include <mutex>
24 #include <string>
25 #include <vector>
26
27 #include <glog/logging.h>
28
29 #include "folly/Foreach.h"
30 #include "folly/Exception.h"
31 #include "folly/Malloc.h"
32
33 // TODO(tudorb): Remove this declaration after Malloc.h is pushed to
34 // third-party.
35 extern "C" int allocm(void**, size_t*, size_t, int)
36 __attribute__((weak));
37
38 namespace folly {
39 namespace threadlocal_detail {
40
41 /**
42  * Base class for deleters.
43  */
44 class DeleterBase {
45  public:
46   virtual ~DeleterBase() { }
47   virtual void dispose(void* ptr, TLPDestructionMode mode) const = 0;
48 };
49
50 /**
51  * Simple deleter class that calls delete on the passed-in pointer.
52  */
53 template <class Ptr>
54 class SimpleDeleter : public DeleterBase {
55  public:
56   virtual void dispose(void* ptr, TLPDestructionMode mode) const {
57     delete static_cast<Ptr>(ptr);
58   }
59 };
60
61 /**
62  * Custom deleter that calls a given callable.
63  */
64 template <class Ptr, class Deleter>
65 class CustomDeleter : public DeleterBase {
66  public:
67   explicit CustomDeleter(Deleter d) : deleter_(d) { }
68   virtual void dispose(void* ptr, TLPDestructionMode mode) const {
69     deleter_(static_cast<Ptr>(ptr), mode);
70   }
71  private:
72   Deleter deleter_;
73 };
74
75
76 /**
77  * POD wrapper around an element (a void*) and an associated deleter.
78  * This must be POD, as we memset() it to 0 and memcpy() it around.
79  */
80 struct ElementWrapper {
81   void dispose(TLPDestructionMode mode) {
82     if (ptr != NULL) {
83       DCHECK(deleter != NULL);
84       deleter->dispose(ptr, mode);
85       if (ownsDeleter) {
86         delete deleter;
87       }
88       ptr = NULL;
89       deleter = NULL;
90       ownsDeleter = false;
91     }
92   }
93
94   template <class Ptr>
95   void set(Ptr p) {
96     DCHECK(ptr == NULL);
97     DCHECK(deleter == NULL);
98
99     if (p) {
100       // We leak a single object here but that is ok.  If we used an
101       // object directly, there is a chance that the destructor will be
102       // called on that static object before any of the ElementWrappers
103       // are disposed and that isn't so nice.
104       static auto d = new SimpleDeleter<Ptr>();
105       ptr = p;
106       deleter = d;
107       ownsDeleter = false;
108     }
109   }
110
111   template <class Ptr, class Deleter>
112   void set(Ptr p, Deleter d) {
113     DCHECK(ptr == NULL);
114     DCHECK(deleter == NULL);
115     if (p) {
116       ptr = p;
117       deleter = new CustomDeleter<Ptr,Deleter>(d);
118       ownsDeleter = true;
119     }
120   }
121
122   void* ptr;
123   DeleterBase* deleter;
124   bool ownsDeleter;
125 };
126
127 /**
128  * Per-thread entry.  Each thread using a StaticMeta object has one.
129  * This is written from the owning thread only (under the lock), read
130  * from the owning thread (no lock necessary), and read from other threads
131  * (under the lock).
132  */
133 struct ThreadEntry {
134   ElementWrapper* elements;
135   size_t elementsCapacity;
136   ThreadEntry* next;
137   ThreadEntry* prev;
138 };
139
140 // Held in a singleton to track our global instances.
141 // We have one of these per "Tag", by default one for the whole system
142 // (Tag=void).
143 //
144 // Creating and destroying ThreadLocalPtr objects, as well as thread exit
145 // for threads that use ThreadLocalPtr objects collide on a lock inside
146 // StaticMeta; you can specify multiple Tag types to break that lock.
147 template <class Tag>
148 struct StaticMeta {
149   static StaticMeta<Tag>& instance() {
150     // Leak it on exit, there's only one per process and we don't have to
151     // worry about synchronization with exiting threads.
152     static bool constructed = (inst_ = new StaticMeta<Tag>());
153     (void)constructed; // suppress unused warning
154     return *inst_;
155   }
156
157   int nextId_;
158   std::vector<int> freeIds_;
159   std::mutex lock_;
160   pthread_key_t pthreadKey_;
161   ThreadEntry head_;
162
163   void push_back(ThreadEntry* t) {
164     t->next = &head_;
165     t->prev = head_.prev;
166     head_.prev->next = t;
167     head_.prev = t;
168   }
169
170   void erase(ThreadEntry* t) {
171     t->next->prev = t->prev;
172     t->prev->next = t->next;
173     t->next = t->prev = t;
174   }
175
176   static __thread ThreadEntry threadEntry_;
177   static StaticMeta<Tag>* inst_;
178
179   StaticMeta() : nextId_(1) {
180     head_.next = head_.prev = &head_;
181     int ret = pthread_key_create(&pthreadKey_, &onThreadExit);
182     checkPosixError(ret, "pthread_key_create failed");
183
184     ret = pthread_atfork(/*prepare*/ &StaticMeta::preFork,
185                          /*parent*/ &StaticMeta::onForkParent,
186                          /*child*/ &StaticMeta::onForkChild);
187     checkPosixError(ret, "pthread_atfork failed");
188   }
189   ~StaticMeta() {
190     LOG(FATAL) << "StaticMeta lives forever!";
191   }
192
193   static void preFork(void) {
194     instance().lock_.lock();  // Make sure it's created
195   }
196
197   static void onForkParent(void) {
198     inst_->lock_.unlock();
199   }
200
201   static void onForkChild(void) {
202     inst_->head_.next = inst_->head_.prev = &inst_->head_;
203     inst_->push_back(&threadEntry_);  // only the current thread survives
204     inst_->lock_.unlock();
205   }
206
207   static void onThreadExit(void* ptr) {
208     auto & meta = instance();
209     DCHECK_EQ(ptr, &meta);
210     // We wouldn't call pthread_setspecific unless we actually called get()
211     DCHECK_NE(threadEntry_.elementsCapacity, 0);
212     {
213       std::lock_guard<std::mutex> g(meta.lock_);
214       meta.erase(&threadEntry_);
215       // No need to hold the lock any longer; threadEntry_ is private to this
216       // thread now that it's been removed from meta.
217     }
218     FOR_EACH_RANGE(i, 0, threadEntry_.elementsCapacity) {
219       threadEntry_.elements[i].dispose(TLPDestructionMode::THIS_THREAD);
220     }
221     free(threadEntry_.elements);
222     threadEntry_.elements = NULL;
223     pthread_setspecific(meta.pthreadKey_, NULL);
224   }
225
226   static int create() {
227     int id;
228     auto & meta = instance();
229     std::lock_guard<std::mutex> g(meta.lock_);
230     if (!meta.freeIds_.empty()) {
231       id = meta.freeIds_.back();
232       meta.freeIds_.pop_back();
233     } else {
234       id = meta.nextId_++;
235     }
236     return id;
237   }
238
239   static void destroy(int id) {
240     try {
241       auto & meta = instance();
242       // Elements in other threads that use this id.
243       std::vector<ElementWrapper> elements;
244       {
245         std::lock_guard<std::mutex> g(meta.lock_);
246         for (ThreadEntry* e = meta.head_.next; e != &meta.head_; e = e->next) {
247           if (id < e->elementsCapacity && e->elements[id].ptr) {
248             elements.push_back(e->elements[id]);
249
250             /*
251              * Writing another thread's ThreadEntry from here is fine;
252              * the only other potential reader is the owning thread --
253              * from onThreadExit (which grabs the lock, so is properly
254              * synchronized with us) or from get(), which also grabs
255              * the lock if it needs to resize the elements vector.
256              *
257              * We can't conflict with reads for a get(id), because
258              * it's illegal to call get on a thread local that's
259              * destructing.
260              */
261             e->elements[id].ptr = nullptr;
262             e->elements[id].deleter = nullptr;
263             e->elements[id].ownsDeleter = false;
264           }
265         }
266         meta.freeIds_.push_back(id);
267       }
268       // Delete elements outside the lock
269       FOR_EACH(it, elements) {
270         it->dispose(TLPDestructionMode::ALL_THREADS);
271       }
272     } catch (...) { // Just in case we get a lock error or something anyway...
273       LOG(WARNING) << "Destructor discarding an exception that was thrown.";
274     }
275   }
276
277   /**
278    * Reserve enough space in the threadEntry_.elements for the item
279    * @id to fit in.
280    */
281   static void reserve(int id) {
282     size_t prevCapacity = threadEntry_.elementsCapacity;
283     // Growth factor < 2, see folly/docs/FBVector.md; + 5 to prevent
284     // very slow start.
285     size_t newCapacity = static_cast<size_t>((id + 5) * 1.7);
286     assert(newCapacity > prevCapacity);
287     auto& meta = instance();
288     ElementWrapper* reallocated = nullptr;
289
290     // Need to grow. Note that we can't call realloc, as elements is
291     // still linked in meta, so another thread might access invalid memory
292     // after realloc succeeds. We'll copy by hand and update threadEntry_
293     // under the lock.
294     if (usingJEMalloc()) {
295       bool success = false;
296       size_t newByteSize = newCapacity * sizeof(ElementWrapper);
297       size_t realByteSize = 0;
298
299       // Try to grow in place.
300       //
301       // Note that rallocm(ALLOCM_ZERO) will only zero newly allocated memory,
302       // even if a previous allocation allocated more than we requested.
303       // This is fine; we always use ALLOCM_ZERO with jemalloc and we
304       // always expand our allocation to the real size.
305       if (prevCapacity * sizeof(ElementWrapper) >=
306           jemallocMinInPlaceExpandable) {
307         success = (rallocm(reinterpret_cast<void**>(&threadEntry_.elements),
308                            &realByteSize,
309                            newByteSize,
310                            0,
311                            ALLOCM_NO_MOVE | ALLOCM_ZERO) == ALLOCM_SUCCESS);
312
313       }
314
315       // In-place growth failed.
316       if (!success) {
317         // Note that, unlike calloc,allocm(... ALLOCM_ZERO) zeros all
318         // allocated bytes (*realByteSize) and not just the requested
319         // bytes (newByteSize)
320         success = (allocm(reinterpret_cast<void**>(&reallocated),
321                           &realByteSize,
322                           newByteSize,
323                           ALLOCM_ZERO) == ALLOCM_SUCCESS);
324       }
325
326       if (success) {
327         // Expand to real size
328         assert(realByteSize / sizeof(ElementWrapper) >= newCapacity);
329         newCapacity = realByteSize / sizeof(ElementWrapper);
330       } else {
331         throw std::bad_alloc();
332       }
333     } else {  // no jemalloc
334       // calloc() is simpler than malloc() followed by memset(), and
335       // potentially faster when dealing with a lot of memory, as it can get
336       // already-zeroed pages from the kernel.
337       reallocated = static_cast<ElementWrapper*>(
338           calloc(newCapacity, sizeof(ElementWrapper)));
339       if (!reallocated) {
340         throw std::bad_alloc();
341       }
342     }
343
344     // Success, update the entry
345     {
346       std::lock_guard<std::mutex> g(meta.lock_);
347
348       if (prevCapacity == 0) {
349         meta.push_back(&threadEntry_);
350       }
351
352       if (reallocated) {
353        /*
354         * Note: we need to hold the meta lock when copying data out of
355         * the old vector, because some other thread might be
356         * destructing a ThreadLocal and writing to the elements vector
357         * of this thread.
358         */
359         memcpy(reallocated, threadEntry_.elements,
360                sizeof(ElementWrapper) * prevCapacity);
361         using std::swap;
362         swap(reallocated, threadEntry_.elements);
363       }
364       threadEntry_.elementsCapacity = newCapacity;
365     }
366
367     free(reallocated);
368
369     if (prevCapacity == 0) {
370       pthread_setspecific(meta.pthreadKey_, &meta);
371     }
372   }
373
374   static ElementWrapper& get(int id) {
375     if (UNLIKELY(threadEntry_.elementsCapacity <= id)) {
376       reserve(id);
377       assert(threadEntry_.elementsCapacity > id);
378     }
379     return threadEntry_.elements[id];
380   }
381 };
382
383 template <class Tag> __thread ThreadEntry StaticMeta<Tag>::threadEntry_ = {0};
384 template <class Tag> StaticMeta<Tag>* StaticMeta<Tag>::inst_ = nullptr;
385
386 }  // namespace threadlocal_detail
387 }  // namespace folly
388
389 #endif /* FOLLY_DETAIL_THREADLOCALDETAIL_H_ */
390