Bug 757969 - use __thread in ThreadLocal; r=froydnj
authorTom Tromey <tom@tromey.com>
Mon, 23 Nov 2015 12:11:22 -0700
changeset 281362 673d16803c0c9caf3a3d2012c7b0cbe74a356b80
parent 281361 1cf358aab01b00bd6e04c492c1a604548063ea19
child 281363 c77c13e5730fa2230ba19bb81a4dca8a10f0a53e
push id29935
push userphilringnalda@gmail.com
push dateSun, 24 Jan 2016 02:12:02 +0000
treeherdermozilla-central@a2e81822194a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs757969
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 757969 - use __thread in ThreadLocal; r=froydnj
dom/base/ScriptSettings.cpp
dom/promise/PromiseDebugging.cpp
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/gl/SkiaGLGlue.cpp
js/src/jit/Ion.cpp
js/src/jsutil.cpp
js/src/vm/Initialization.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
mfbt/ThreadLocal.h
tools/memory-profiler/UncensoredAllocator.cpp
tools/memory-profiler/UncensoredAllocator.h
tools/profiler/core/platform.cpp
tools/profiler/public/GeckoProfilerImpl.h
tools/profiler/tasktracer/GeckoTaskTracer.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/build/IOInterposer.cpp
xpcom/threads/AbstractThread.cpp
xpcom/threads/AbstractThread.h
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/nsThreadManager.cpp
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -21,17 +21,18 @@
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsDOMJSUtils.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
-static mozilla::ThreadLocal<ScriptSettingsStackEntry*> sScriptSettingsTLS;
+static MOZ_THREAD_LOCAL(ScriptSettingsStackEntry*) sScriptSettingsTLS;
+static bool sScriptSettingsTLSInitialized;
 
 class ScriptSettingsStack {
 public:
   static ScriptSettingsStackEntry* Top() {
     return sScriptSettingsTLS.get();
   }
 
   static void Push(ScriptSettingsStackEntry *aEntry) {
@@ -89,36 +90,35 @@ UnuseEntryScriptProfiling()
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(gRunToCompletionListeners > 0);
   --gRunToCompletionListeners;
 }
 
 void
 InitScriptSettings()
 {
-  if (!sScriptSettingsTLS.initialized()) {
-    bool success = sScriptSettingsTLS.init();
-    if (!success) {
-      MOZ_CRASH();
-    }
+  bool success = sScriptSettingsTLS.init();
+  if (!success) {
+    MOZ_CRASH();
   }
 
   sScriptSettingsTLS.set(nullptr);
+  sScriptSettingsTLSInitialized = true;
 }
 
 void
 DestroyScriptSettings()
 {
   MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr);
 }
 
 bool
 ScriptSettingsInitialized()
 {
-  return sScriptSettingsTLS.initialized();
+  return sScriptSettingsTLSInitialized;
 }
 
 ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject *aGlobal,
                                                    bool aCandidate)
   : mGlobalObject(aGlobal)
   , mIsCandidateEntryPoint(aCandidate)
   , mOlder(nullptr)
 {
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -55,20 +55,20 @@ public:
   NS_IMETHOD Run() {
     FlushSync();
     return NS_OK;
   }
 
 private:
   // `true` if an instance of `FlushRejections` is currently dispatched
   // and has not been executed yet.
-  static ThreadLocal<bool> sDispatched;
+  static MOZ_THREAD_LOCAL(bool) sDispatched;
 };
 
-/* static */ ThreadLocal<bool>
+/* static */ MOZ_THREAD_LOCAL(bool)
 FlushRejections::sDispatched;
 
 static Promise*
 UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv)
 {
   Promise* promise;
   if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) {
     aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING("Argument"));
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -24,17 +24,17 @@
 #include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
 StaticMutex GLLibraryEGL::sMutex;
 GLLibraryEGL sEGLLibrary;
 #ifdef MOZ_B2G
-ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext;
+MOZ_THREAD_LOCAL(EGLContext) GLLibraryEGL::sCurrentContext;
 #endif
 
 // should match the order of EGLExtensions, and be null-terminated.
 static const char *sEGLExtensionNames[] = {
     "EGL_KHR_image_base",
     "EGL_KHR_image_pixmap",
     "EGL_KHR_gl_texture_2D_image",
     "EGL_KHR_lock_surface",
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -636,17 +636,17 @@ public:
     void SetCachedCurrentContext(EGLContext aCtx) {
         sCurrentContext.set(aCtx);
     }
     bool CachedCurrentContextMatches() {
         return sCurrentContext.get() == fGetCurrentContext();
     }
 
 private:
-    static ThreadLocal<EGLContext> sCurrentContext;
+    static MOZ_THREAD_LOCAL(EGLContext) sCurrentContext;
 public:
 
 #else
     EGLContext CachedCurrentContext() {
         return nullptr;
     }
     void UnsetCachedCurrentContext() {}
     void SetCachedCurrentContext(EGLContext aCtx) { }
--- a/gfx/gl/SkiaGLGlue.cpp
+++ b/gfx/gl/SkiaGLGlue.cpp
@@ -19,26 +19,24 @@
 #include "GLContext.h"
 #include "SkiaGLGlue.h"
 
 using mozilla::gl::GLContext;
 using mozilla::gl::GLFeature;
 using mozilla::gl::SkiaGLGlue;
 using mozilla::gfx::DrawTarget;
 
-static mozilla::ThreadLocal<GLContext*> sGLContext;
+static MOZ_THREAD_LOCAL(GLContext*) sGLContext;
 
 extern "C" {
 
 static void SetStaticGLContext(GLContext* context)
 {
-    if (!sGLContext.initialized()) {
-      mozilla::DebugOnly<bool> success = sGLContext.init();
-      MOZ_ASSERT(success);
-    }
+    mozilla::DebugOnly<bool> success = sGLContext.init();
+    MOZ_ASSERT(success);
 
     sGLContext.set(context);
 }
 
 void EnsureGLContext(const GrGLInterface* i)
 {
     const SkiaGLGlue* contextSkia = reinterpret_cast<const SkiaGLGlue*>(i->fCallbackData);
     MOZ_ASSERT(contextSkia);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -53,27 +53,25 @@
 #include "jit/JitFrames-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
-using mozilla::ThreadLocal;
-
 // Assert that JitCode is gc::Cell aligned.
 JS_STATIC_ASSERT(sizeof(JitCode) % gc::CellSize == 0);
 
-static ThreadLocal<JitContext*> TlsJitContext;
+static MOZ_THREAD_LOCAL(JitContext*) TlsJitContext;
 
 static JitContext*
 CurrentJitContext()
 {
-    if (!TlsJitContext.initialized())
+    if (!TlsJitContext.init())
         return nullptr;
     return TlsJitContext.get();
 }
 
 void
 jit::SetJitContext(JitContext* ctx)
 {
     TlsJitContext.set(ctx);
@@ -150,17 +148,17 @@ JitContext::JitContext(CompileRuntime* r
 JitContext::~JitContext()
 {
     SetJitContext(prev_);
 }
 
 bool
 jit::InitializeIon()
 {
-    if (!TlsJitContext.initialized() && !TlsJitContext.init())
+    if (!TlsJitContext.init())
         return false;
     CheckLogging();
 #if defined(JS_CODEGEN_ARM)
     InitARMFlags();
 #endif
     CheckPerf();
     return true;
 }
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -32,21 +32,21 @@ using mozilla::PodArrayZero;
 /* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */
 JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX;
 JS_PUBLIC_DATA(uint32_t) OOM_counter = 0;
 JS_PUBLIC_DATA(bool) OOM_failAlways = true;
 namespace js {
 namespace oom {
 
 JS_PUBLIC_DATA(uint32_t) targetThread = 0;
-JS_PUBLIC_DATA(mozilla::ThreadLocal<uint32_t>) threadType;
+JS_PUBLIC_DATA(MOZ_THREAD_LOCAL(uint32_t)) threadType;
 
 bool
 InitThreadType(void) {
-    return threadType.initialized() || threadType.init();
+    return threadType.init();
 }
 
 void
 SetThreadType(ThreadType type) {
     threadType.set(type);
 }
 
 uint32_t
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -70,17 +70,17 @@ JS_Init(void)
 
     PRMJ_NowInit();
 
 #ifdef DEBUG
     CheckMessageParameterCounts();
 #endif
 
     using js::TlsPerThreadData;
-    if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
+    if (!TlsPerThreadData.init())
         return false;
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     if (!js::oom::InitThreadType())
         return false;
     js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
 #endif
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -58,21 +58,20 @@ using namespace js;
 using namespace js::gc;
 
 using mozilla::Atomic;
 using mozilla::DebugOnly;
 using mozilla::NegativeInfinity;
 using mozilla::PodZero;
 using mozilla::PodArrayZero;
 using mozilla::PositiveInfinity;
-using mozilla::ThreadLocal;
 using JS::GenericNaN;
 using JS::DoubleNaNValue;
 
-/* static */ ThreadLocal<PerThreadData*> js::TlsPerThreadData;
+/* static */ MOZ_THREAD_LOCAL(PerThreadData*) js::TlsPerThreadData;
 /* static */ Atomic<size_t> JSRuntime::liveRuntimesCount;
 
 namespace js {
     bool gCanUseExtraThreads = true;
 } // namespace js
 
 void
 js::DisableExtraThreads()
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -58,17 +58,17 @@ namespace js {
 class PerThreadData;
 class ExclusiveContext;
 class AutoKeepAtoms;
 #ifdef JS_TRACE_LOGGING
 class TraceLoggerThread;
 #endif
 
 /* Thread Local Storage slot for storing the runtime for a thread. */
-extern mozilla::ThreadLocal<PerThreadData*> TlsPerThreadData;
+extern MOZ_THREAD_LOCAL(PerThreadData*) TlsPerThreadData;
 
 } // namespace js
 
 struct DtoaState;
 
 #ifdef JS_SIMULATOR_ARM64
 namespace vixl {
 class Simulator;
--- a/mfbt/ThreadLocal.h
+++ b/mfbt/ThreadLocal.h
@@ -37,46 +37,65 @@ namespace mozilla {
 // instruction.  This means that data of this type is safe to be manipulated
 // from a signal handler, or other similar asynchronous execution contexts.
 #if defined(XP_WIN)
 typedef unsigned long sig_safe_t;
 #else
 typedef sig_atomic_t sig_safe_t;
 #endif
 
+namespace detail {
+
+#if defined(HAVE_THREAD_TLS_KEYWORD)
+#define MOZ_HAS_THREAD_LOCAL
+#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 exactly once, before any usage of set()
- * // or get().
- * mozilla::ThreadLocal<int> tlsKey;
+ * // 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();
  */
 template<typename T>
 class ThreadLocal
 {
+#ifndef MOZ_HAS_THREAD_LOCAL
 #if defined(XP_WIN)
   typedef unsigned long key_t;
 #else
   typedef pthread_key_t key_t;
 #endif
 
   // Integral types narrower than void* must be extended to avoid
   // warnings from valgrind on some platforms.  This helper type
@@ -88,80 +107,116 @@ class ThreadLocal
     typedef uintptr_t Type;
   };
 
   template<typename S>
   struct Helper<S *>
   {
     typedef S *Type;
   };
+#endif
+
+  bool initialized() const {
+#ifdef MOZ_HAS_THREAD_LOCAL
+    return true;
+#else
+    return mInited;
+#endif
+  }
 
 public:
+  // __thread does not allow non-trivial constructors, but we can
+  // instead rely on zero-initialization.
+#ifndef MOZ_HAS_THREAD_LOCAL
   ThreadLocal()
     : mKey(0), mInited(false)
   {}
+#endif
 
   MOZ_WARN_UNUSED_RESULT inline bool init();
 
   inline T get() const;
 
   inline void set(const T aValue);
 
-  bool initialized() const { return mInited; }
-
 private:
+#ifdef MOZ_HAS_THREAD_LOCAL
+  T mValue;
+#else
   key_t mKey;
   bool mInited;
+#endif
 };
 
 template<typename T>
 inline bool
 ThreadLocal<T>::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");
-  MOZ_ASSERT(!initialized());
+
+#ifdef MOZ_HAS_THREAD_LOCAL
+  return true;
+#else
+  if (!initialized()) {
 #ifdef XP_WIN
-  mKey = TlsAlloc();
-  mInited = mKey != 0xFFFFFFFFUL; // TLS_OUT_OF_INDEXES
+    mKey = TlsAlloc();
+    mInited = mKey != 0xFFFFFFFFUL; // TLS_OUT_OF_INDEXES
 #else
-  mInited = !pthread_key_create(&mKey, nullptr);
+    mInited = !pthread_key_create(&mKey, nullptr);
 #endif
+  }
   return mInited;
+#endif
 }
 
 template<typename T>
 inline T
 ThreadLocal<T>::get() const
 {
+#ifdef MOZ_HAS_THREAD_LOCAL
+  return mValue;
+#else
   MOZ_ASSERT(initialized());
   void* h;
 #ifdef XP_WIN
   h = TlsGetValue(mKey);
 #else
   h = pthread_getspecific(mKey);
 #endif
   return static_cast<T>(reinterpret_cast<typename Helper<T>::Type>(h));
+#endif
 }
 
 template<typename T>
 inline void
 ThreadLocal<T>::set(const T aValue)
 {
+#ifdef MOZ_HAS_THREAD_LOCAL
+  mValue = aValue;
+#else
   MOZ_ASSERT(initialized());
   void* h = reinterpret_cast<void*>(static_cast<typename Helper<T>::Type>(aValue));
 #ifdef XP_WIN
   bool succeeded = TlsSetValue(mKey, h);
 #else
   bool succeeded = !pthread_setspecific(mKey, h);
 #endif
   if (!succeeded) {
     MOZ_CRASH();
   }
+#endif
 }
 
+#ifdef MOZ_HAS_THREAD_LOCAL
+#define MOZ_THREAD_LOCAL(TYPE) __thread mozilla::detail::ThreadLocal<TYPE>
+#else
+#define MOZ_THREAD_LOCAL(TYPE) mozilla::detail::ThreadLocal<TYPE>
+#endif
+
+} // namespace detail
 } // namespace mozilla
 
 #endif /* mozilla_ThreadLocal_h */
--- a/tools/memory-profiler/UncensoredAllocator.cpp
+++ b/tools/memory-profiler/UncensoredAllocator.cpp
@@ -15,17 +15,17 @@
 #include "prlock.h"
 #ifdef MOZ_REPLACE_MALLOC
 #include "replace_malloc_bridge.h"
 #endif
 
 namespace mozilla {
 
 #ifdef MOZ_REPLACE_MALLOC
-ThreadLocal<bool> MallocHook::mEnabledTLS;
+MOZ_THREAD_LOCAL(bool) MallocHook::mEnabledTLS;
 NativeProfiler* MallocHook::mNativeProfiler;
 malloc_hook_table_t MallocHook::mMallocHook;
 #endif
 
 AutoUseUncensoredAllocator::AutoUseUncensoredAllocator()
 {
 #ifdef MOZ_REPLACE_MALLOC
   MallocHook::mEnabledTLS.set(false);
@@ -76,34 +76,29 @@ MallocHook::Initialize()
 #ifdef MOZ_REPLACE_MALLOC
   MOZ_ASSERT(NS_IsMainThread());
   mMallocHook.free_hook = RemoveNative;
   mMallocHook.malloc_hook = SampleNative;
   ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
   if (bridge) {
     mozilla::Unused << bridge->RegisterHook("memory-profiler", nullptr, nullptr);
   }
-  if (!mEnabledTLS.initialized()) {
-    bool success = mEnabledTLS.init();
-    if (NS_WARN_IF(!success)) {
-      return;
-    }
-    mEnabledTLS.set(false);
+
+  bool success = mEnabledTLS.init();
+  if (NS_WARN_IF(!success)) {
+    return;
   }
 #endif
 }
 
 void
 MallocHook::Enable(NativeProfiler* aNativeProfiler)
 {
 #ifdef MOZ_REPLACE_MALLOC
   MOZ_ASSERT(NS_IsMainThread());
-  if (NS_WARN_IF(!mEnabledTLS.initialized())) {
-    return;
-  }
   ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
   if (bridge) {
     const malloc_table_t* alloc_funcs =
       bridge->RegisterHook("memory-profiler", nullptr, &mMallocHook);
     if (alloc_funcs) {
       mNativeProfiler = aNativeProfiler;
     }
   }
--- a/tools/memory-profiler/UncensoredAllocator.h
+++ b/tools/memory-profiler/UncensoredAllocator.h
@@ -24,17 +24,17 @@ public:
   static void Initialize();
   static void Enable(NativeProfiler* aNativeProfiler);
   static void Disable();
   static bool Enabled();
 private:
   static void* SampleNative(void* aAddr, size_t aSize);
   static void RemoveNative(void* aAddr);
 #ifdef MOZ_REPLACE_MALLOC
-  static ThreadLocal<bool> mEnabledTLS;
+  static MOZ_THREAD_LOCAL(bool) mEnabledTLS;
   static NativeProfiler* mNativeProfiler;
   static malloc_hook_table_t mMallocHook;
 #endif
   friend class AutoUseUncensoredAllocator;
 };
 
 class MOZ_RAII AutoUseUncensoredAllocator final
 {
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -62,19 +62,19 @@ public:
     if (!profiler_is_active()) {
       return 0.0;
     }
     return profiler_time();
   };
 };
 #endif
 
-mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
-mozilla::ThreadLocal<GeckoSampler *> tlsTicker;
-mozilla::ThreadLocal<void *> tlsStackTop;
+MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
+MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
+MOZ_THREAD_LOCAL(void *) tlsStackTop;
 // We need to track whether we've been initialized otherwise
 // we end up using tlsStack without initializing it.
 // Because tlsStack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
 
 mozilla::TimeStamp   sLastTracerEvent; // is raced on
 mozilla::TimeStamp   sStartTime;
--- a/tools/profiler/public/GeckoProfilerImpl.h
+++ b/tools/profiler/public/GeckoProfilerImpl.h
@@ -35,19 +35,19 @@
 #endif
 
 class GeckoSampler;
 
 namespace mozilla {
 class TimeStamp;
 } // namespace mozilla
 
-extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
-extern mozilla::ThreadLocal<GeckoSampler *> tlsTicker;
-extern mozilla::ThreadLocal<void *> tlsStackTop;
+extern MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
+extern MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
+extern MOZ_THREAD_LOCAL(void *) tlsStackTop;
 extern bool stack_key_initialized;
 
 #ifndef SAMPLE_FUNCTION_NAME
 # ifdef __GNUC__
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # elif defined(_MSC_VER)
 #  define SAMPLE_FUNCTION_NAME __FUNCTION__
 # else
--- a/tools/profiler/tasktracer/GeckoTaskTracer.cpp
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -54,17 +54,17 @@ pid_t gettid();
     if (MOZ_UNLIKELY(!(x))) { \
        return ret;            \
     }                         \
   } while(0)
 
 namespace mozilla {
 namespace tasktracer {
 
-static mozilla::ThreadLocal<TraceInfo*> sTraceInfoTLS;
+static MOZ_THREAD_LOCAL(TraceInfo*) sTraceInfoTLS;
 static mozilla::StaticMutex sMutex;
 
 // The generation of TraceInfo. It will be > 0 if the Task Tracer is started and
 // <= 0 if stopped.
 static mozilla::Atomic<bool> sStarted;
 static nsTArray<nsAutoPtr<TraceInfo>>* sTraceInfos = nullptr;
 static PRTime sStartTime;
 
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -1404,17 +1404,17 @@ public:
 ////////////////////////////////////////////////////////////////////////
 
 struct CollectorData
 {
   RefPtr<nsCycleCollector> mCollector;
   CycleCollectedJSRuntime* mRuntime;
 };
 
-static mozilla::ThreadLocal<CollectorData*> sCollectorData;
+static MOZ_THREAD_LOCAL(CollectorData*) sCollectorData;
 
 ////////////////////////////////////////////////////////////////////////
 // Utility functions
 ////////////////////////////////////////////////////////////////////////
 
 static inline void
 ToParticipant(nsISupports* aPtr, nsXPCOMCycleCollectionParticipant** aCp)
 {
@@ -3994,27 +3994,28 @@ nsCycleCollector_suspectedCount()
   }
 
   return data->mCollector->SuspectedCount();
 }
 
 bool
 nsCycleCollector_init()
 {
+  static DebugOnly<bool> sInitialized;
+
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(!sCollectorData.initialized(), "Called twice!?");
+  MOZ_ASSERT(!sInitialized, "Called twice!?");
+  sInitialized = true;
 
   return sCollectorData.init();
 }
 
 void
 nsCycleCollector_startup()
 {
-  MOZ_ASSERT(sCollectorData.initialized(),
-             "Forgot to call nsCycleCollector_init!");
   if (sCollectorData.get()) {
     MOZ_CRASH();
   }
 
   CollectorData* data = new CollectorData;
   data->mCollector = new nsCycleCollector();
   data->mRuntime = nullptr;
 
--- a/xpcom/build/IOInterposer.cpp
+++ b/xpcom/build/IOInterposer.cpp
@@ -354,17 +354,18 @@ public:
   {
     mStart = TimeStamp::Now();
     mEnd = mStart;
   }
 };
 
 // List of observers registered
 static StaticAutoPtr<MasterList> sMasterList;
-static ThreadLocal<PerThreadData*> sThreadLocalData;
+static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
+static bool sThreadLocalDataInitialized;
 } // namespace
 
 IOInterposeObserver::Observation::Observation(Operation aOperation,
                                               const char* aReference,
                                               bool aShouldReport)
   : mOperation(aOperation)
   , mReference(aReference)
   , mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
@@ -424,16 +425,17 @@ IOInterposer::Init()
 {
   // Don't initialize twice...
   if (sMasterList) {
     return true;
   }
   if (!sThreadLocalData.init()) {
     return false;
   }
+  sThreadLocalDataInitialized = true;
   bool isMainThread = true;
   RegisterCurrentThread(isMainThread);
   sMasterList = new MasterList();
 
   MainThreadIOLogger::Init();
 
   // Now we initialize the various interposers depending on platform
   InitPoisonIOInterposer();
@@ -443,17 +445,17 @@ IOInterposer::Init()
   InitNSPRIOInterposing();
 #endif
   return true;
 }
 
 bool
 IOInterposeObserver::IsMainThread()
 {
-  if (!sThreadLocalData.initialized()) {
+  if (!sThreadLocalDataInitialized) {
     return false;
   }
   PerThreadData* ptd = sThreadLocalData.get();
   if (!ptd) {
     return false;
   }
   return ptd->IsMainThread();
 }
@@ -533,28 +535,28 @@ IOInterposer::Unregister(IOInterposeObse
   }
 
   sMasterList->Unregister(aOp, aObserver);
 }
 
 void
 IOInterposer::RegisterCurrentThread(bool aIsMainThread)
 {
-  if (!sThreadLocalData.initialized()) {
+  if (!sThreadLocalDataInitialized) {
     return;
   }
   MOZ_ASSERT(!sThreadLocalData.get());
   PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
   sThreadLocalData.set(curThreadData);
 }
 
 void
 IOInterposer::UnregisterCurrentThread()
 {
-  if (!sThreadLocalData.initialized()) {
+  if (!sThreadLocalDataInitialized) {
     return;
   }
   PerThreadData* curThreadData = sThreadLocalData.get();
   MOZ_ASSERT(curThreadData);
   sThreadLocalData.set(nullptr);
   delete curThreadData;
 }
 
--- a/xpcom/threads/AbstractThread.cpp
+++ b/xpcom/threads/AbstractThread.cpp
@@ -21,17 +21,17 @@
 
 
 namespace mozilla {
 
 LazyLogModule gMozPromiseLog("MozPromise");
 LazyLogModule gStateWatchingLog("StateWatching");
 
 StaticRefPtr<AbstractThread> sMainThread;
-ThreadLocal<AbstractThread*> AbstractThread::sCurrentThreadTLS;
+MOZ_THREAD_LOCAL(AbstractThread*) AbstractThread::sCurrentThreadTLS;
 
 class XPCOMThreadWrapper : public AbstractThread
 {
 public:
   explicit XPCOMThreadWrapper(nsIThread* aTarget, bool aRequireTailDispatch)
     : AbstractThread(aRequireTailDispatch)
     , mTarget(aTarget)
   {
--- a/xpcom/threads/AbstractThread.h
+++ b/xpcom/threads/AbstractThread.h
@@ -80,17 +80,17 @@ public:
   static void InitStatics();
 
   void DispatchStateChange(already_AddRefed<nsIRunnable> aRunnable);
 
   static void DispatchDirectTask(already_AddRefed<nsIRunnable> aRunnable);
 
 protected:
   virtual ~AbstractThread() {}
-  static ThreadLocal<AbstractThread*> sCurrentThreadTLS;
+  static MOZ_THREAD_LOCAL(AbstractThread*) sCurrentThreadTLS;
 
   // True if we want to require that every task dispatched from tasks running in
   // this queue go through our queue's tail dispatcher.
   const bool mSupportsTailDispatch;
 };
 
 already_AddRefed<AbstractThread> CreateXPCOMAbstractThreadWrapper(nsIThread* aThread,
                                  bool aRequireTailDispatch);
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -133,17 +133,18 @@ BackgroundHangManager::Observe(nsISuppor
 
 /**
  * BackgroundHangThread is a per-thread object that is used
  * by all instances of BackgroundHangMonitor to monitor hangs.
  */
 class BackgroundHangThread : public LinkedListElement<BackgroundHangThread>
 {
 private:
-  static ThreadLocal<BackgroundHangThread*> sTlsKey;
+  static MOZ_THREAD_LOCAL(BackgroundHangThread*) sTlsKey;
+  static bool sTlsKeyInitialized;
 
   BackgroundHangThread(const BackgroundHangThread&);
   BackgroundHangThread& operator=(const BackgroundHangThread&);
   ~BackgroundHangThread();
 
   /* Keep a reference to the manager, so we can keep going even
      after BackgroundHangManager::Shutdown is called. */
   const RefPtr<BackgroundHangManager> mManager;
@@ -201,18 +202,18 @@ public:
   }
 };
 
 
 StaticRefPtr<BackgroundHangManager> BackgroundHangManager::sInstance;
 bool BackgroundHangManager::sProhibited = false;
 bool BackgroundHangManager::sDisabled = false;
 
-ThreadLocal<BackgroundHangThread*> BackgroundHangThread::sTlsKey;
-
+MOZ_THREAD_LOCAL(BackgroundHangThread*) BackgroundHangThread::sTlsKey;
+bool BackgroundHangThread::sTlsKeyInitialized;
 
 BackgroundHangManager::BackgroundHangManager()
   : mShutdown(false)
   , mLock("BackgroundHangManager")
   , mIntervalNow(0)
 {
   // Lock so we don't race against the new monitor thread
   MonitorAutoLock autoLock(mLock);
@@ -367,17 +368,17 @@ BackgroundHangThread::BackgroundHangThre
                 ? PR_INTERVAL_NO_TIMEOUT
                 : PR_MillisecondsToInterval(aMaxTimeoutMs))
   , mInterval(mManager->mIntervalNow)
   , mHangStart(mInterval)
   , mHanging(false)
   , mWaiting(true)
   , mStats(aName)
 {
-  if (sTlsKey.initialized()) {
+  if (sTlsKeyInitialized) {
     sTlsKey.set(this);
   }
   // Lock here because LinkedList is not thread-safe
   MonitorAutoLock autoLock(mManager->mLock);
   // Add to thread list
   mManager->mHangThreads.insertBack(this);
   // Wake up monitor thread to process new thread
   autoLock.Notify();
@@ -388,17 +389,17 @@ BackgroundHangThread::~BackgroundHangThr
   // Lock here because LinkedList is not thread-safe
   MonitorAutoLock autoLock(mManager->mLock);
   // Remove from thread list
   remove();
   // Wake up monitor thread to process removed thread
   autoLock.Notify();
 
   // We no longer have a thread
-  if (sTlsKey.initialized()) {
+  if (sTlsKeyInitialized) {
     sTlsKey.set(nullptr);
   }
 
   // Move our copy of ThreadHangStats to Telemetry storage
   Telemetry::RecordThreadHangStats(mStats);
 }
 
 Telemetry::HangHistogram&
@@ -484,17 +485,17 @@ BackgroundHangThread::FindThread()
 {
 #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR
   if (BackgroundHangManager::sInstance == nullptr) {
     MOZ_ASSERT(BackgroundHangManager::sProhibited || BackgroundHangManager::sDisabled,
                "BackgroundHandleManager is not initialized");
     return nullptr;
   }
 
-  if (sTlsKey.initialized()) {
+  if (sTlsKeyInitialized) {
     // Use TLS if available
     MOZ_ASSERT(!BackgroundHangManager::sProhibited,
                "BackgroundHandleManager is not initialized");
     return sTlsKey.get();
   }
   // If TLS is unavailable, we can search through the thread list
   RefPtr<BackgroundHangManager> manager(BackgroundHangManager::sInstance);
   MOZ_ASSERT(manager, "Creating BackgroundHangMonitor after shutdown");
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -13,33 +13,31 @@
 #include "mozilla/ThreadLocal.h"
 #ifdef MOZ_CANARY
 #include <fcntl.h>
 #include <unistd.h>
 #endif
 
 using namespace mozilla;
 
-static mozilla::ThreadLocal<bool> sTLSIsMainThread;
+static MOZ_THREAD_LOCAL(bool) sTLSIsMainThread;
 
 bool
 NS_IsMainThread()
 {
   return sTLSIsMainThread.get();
 }
 
 void
 NS_SetMainThread()
 {
-  if (!sTLSIsMainThread.initialized()) {
-    if (!sTLSIsMainThread.init()) {
-      MOZ_CRASH();
-    }
-    sTLSIsMainThread.set(true);
+  if (!sTLSIsMainThread.init()) {
+    MOZ_CRASH();
   }
+  sTLSIsMainThread.set(true);
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 typedef nsTArray<RefPtr<nsThread>> nsThreadArray;
 
 //-----------------------------------------------------------------------------
 
 static void