Revision control

Line Code
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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* Cross-platform lightweight thread local data wrappers. */

#ifndef mozilla_ThreadLocal_h
#define mozilla_ThreadLocal_h

#if !defined(XP_WIN)
#  include <pthread.h>
#endif

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/TypeTraits.h"

namespace mozilla {

namespace detail {

#ifdef XP_MACOSX
#  if defined(__has_feature)
#    if __has_feature(cxx_thread_local)
#      define MACOSX_HAS_THREAD_LOCAL
#    endif
#  endif
#endif

/*
 * Thread Local Storage helpers.
 *
 * Usage:
 *
 * Do not directly instantiate this class.  Instead, use the
 * MOZ_THREAD_LOCAL macro to declare or define instances.  The macro
 * takes a type name as its argument.
 *
 * Declare like this:
 * extern MOZ_THREAD_LOCAL(int) tlsInt;
 * Define like this:
 * MOZ_THREAD_LOCAL(int) tlsInt;
 * or:
 * static MOZ_THREAD_LOCAL(int) tlsInt;
 *
 * Only static-storage-duration (e.g. global variables, or static class members)
 * objects of this class should be instantiated. This class relies on
 * zero-initialization, which is implicit for static-storage-duration objects.
 * It doesn't have a custom default constructor, to avoid static initializers.
 *
 * API usage:
 *
 * // Create a TLS item.
 * //
 * // Note that init() should be invoked before the first use of set()
 * // or get().  It is ok to call it multiple times.  This must be
 * // called in a way that avoids possible races with other threads.
 * MOZ_THREAD_LOCAL(int) tlsKey;
 * if (!tlsKey.init()) {
 *   // deal with the error
 * }
 *
 * // Set the TLS value
 * tlsKey.set(123);
 *
 * // Get the TLS value
 * int value = tlsKey.get();
 */

// Integral types narrower than void* must be extended to avoid
// warnings from valgrind on some platforms.  This helper type
// achieves that without penalizing the common case of ThreadLocals
// instantiated using a pointer type.
template<typename S>
struct Helper
{
  typedef uintptr_t Type;
};

template<typename S>
struct Helper<S *>
{
  typedef S *Type;
};

#ifdef XP_WIN
/* Despite not being used for MOZ_THREAD_LOCAL, we expose an implementation for
 * Windows for cases where it's not desirable to use thread_local */
template<typename T>
class ThreadLocalKeyStorage
{
public:
  ThreadLocalKeyStorage()
    : mKey(TLS_OUT_OF_INDEXES)
  {}

  inline bool initialized() const {
    return mKey != TLS_OUT_OF_INDEXES;
  }

  inline void init() {
    mKey = TlsAlloc();
  }

  inline T get() const {
    void* h = TlsGetValue(mKey);
    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
  }

  inline bool set(const T aValue) {
    void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
    return TlsSetValue(mKey, h);
  }

private:
  unsigned long mKey;
};
#else
template<typename T>
class ThreadLocalKeyStorage
{
public:
  ThreadLocalKeyStorage()
    : mKey(0), mInited(false)
  {}

  inline bool initialized() const {
    return mInited;
  }

  inline void init() {
    mInited = !pthread_key_create(&mKey, nullptr);
  }

  inline T get() const {
    void* h = pthread_getspecific(mKey);
    return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
  }

  inline bool set(const T aValue) {
    void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
    return !pthread_setspecific(mKey, h);
  }

private:
  pthread_key_t mKey;
  bool mInited;
};
#endif

template<typename T>
class ThreadLocalNativeStorage
{
public:
  // __thread does not allow non-trivial constructors, but we can
  // instead rely on zero-initialization.
  inline bool initialized() const {
    return true;
  }

  inline void init() {
  }

  inline T get() const {
    return mValue;
  }

  inline bool set(const T aValue) {
    mValue = aValue;
    return true;
  }

private:
  T mValue;
};

template<typename T, template <typename U> class Storage>
class ThreadLocal: public Storage<T>
{
public:
  MOZ_MUST_USE inline bool init();

  void infallibleInit() {
    MOZ_RELEASE_ASSERT(init(), "Infallible TLS initialization failed");
  }

  inline T get() const;

  inline void set(const T aValue);
};

template<typename T, template <typename U> class Storage>
inline bool
ThreadLocal<T, Storage>::init()
{
  static_assert(mozilla::IsPointer<T>::value || mozilla::IsIntegral<T>::value,
                "mozilla::ThreadLocal must be used with a pointer or "
                "integral type");
  static_assert(sizeof(T) <= sizeof(void*),
                "mozilla::ThreadLocal can't be used for types larger than "
                "a pointer");

  if (!Storage<T>::initialized()) {
    Storage<T>::init();
  }
  return Storage<T>::initialized();
}

template<typename T, template <typename U> class Storage>
inline T
ThreadLocal<T, Storage>::get() const
{
  MOZ_ASSERT(Storage<T>::initialized());
  return Storage<T>::get();
}

template<typename T, template <typename U> class Storage>
inline void
ThreadLocal<T, Storage>::set(const T aValue)
{
  MOZ_ASSERT(Storage<T>::initialized());
  bool succeeded = Storage<T>::set(aValue);
  if (!succeeded) {
    MOZ_CRASH();
  }
}

#if defined(XP_WIN) || defined(MACOSX_HAS_THREAD_LOCAL)
#define MOZ_THREAD_LOCAL(TYPE) thread_local mozilla::detail::ThreadLocal<TYPE, mozilla::detail::ThreadLocalNativeStorage>
#elif defined(HAVE_THREAD_TLS_KEYWORD)
#define MOZ_THREAD_LOCAL(TYPE) __thread mozilla::detail::ThreadLocal<TYPE, mozilla::detail::ThreadLocalNativeStorage>
#else
#define MOZ_THREAD_LOCAL(TYPE) mozilla::detail::ThreadLocal<TYPE, mozilla::detail::ThreadLocalKeyStorage>
#endif

} // namespace detail
} // namespace mozilla

#endif /* mozilla_ThreadLocal_h */