/
ThreadLocal.h
350 lines (293 loc) · 9.46 KB
/
ThreadLocal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
* Copyright 2012 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>
// Use noexcept on gcc 4.6 or higher
#undef FOLLY_NOEXCEPT
#ifdef __GNUC__
# ifdef HAVE_FEATURES_H
# include <features.h>
# if __GNUC_PREREQ(4,6)
# define FOLLY_NOEXCEPT noexcept
# define FOLLY_ASSERT(x) x
# endif
# endif
#endif
#ifndef FOLLY_NOEXCEPT
# define FOLLY_NOEXCEPT
# define FOLLY_ASSERT(x) /**/
#endif
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() { }
T* get() const {
T* ptr = tlp_.get();
if (UNLIKELY(ptr == nullptr)) {
ptr = new T();
tlp_.reset(ptr);
}
return ptr;
}
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;
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.
*/
template<class T, class Tag=void>
class ThreadLocalPtr {
public:
ThreadLocalPtr() : id_(threadlocal_detail::StaticMeta<Tag>::create()) { }
ThreadLocalPtr(ThreadLocalPtr&& other) : 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();
}
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_;
boost::mutex* lock_;
int 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) FOLLY_NOEXCEPT
: meta_(other.meta_),
lock_(other.lock_),
id_(other.id_) {
other.id_ = 0;
other.lock_ = nullptr;
}
Accessor& operator=(Accessor&& other) FOLLY_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(int 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 {
FOLLY_ASSERT(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;
int id_; // every instantiation has a unique id
};
#undef FOLLY_NOEXCEPT
} // namespace folly
#endif /* FOLLY_THREADLOCAL_H_ */