SafeThreadLocal.h (3273B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_freestanding_SafeThreadLocal_h 8 #define mozilla_freestanding_SafeThreadLocal_h 9 10 #include <type_traits> 11 12 #include "mozilla/NativeNt.h" 13 #include "mozilla/ThreadLocal.h" 14 15 namespace mozilla { 16 namespace freestanding { 17 18 // We cannot fall back to the Tls* APIs because kernel32 might not have been 19 // loaded yet. 20 #if defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD) 21 # error "This code requires the compiler to have native TLS support" 22 #endif // defined(__MINGW32__) && !defined(HAVE_THREAD_TLS_KEYWORD) 23 24 /** 25 * This class holds data as a thread-local variable, or as a global variable 26 * if the thread local storage is not initialized yet. It should be safe 27 * because in that early stage we assume there is no more than a single thread. 28 */ 29 template <typename T> 30 class SafeThreadLocal final { 31 static MOZ_THREAD_LOCAL(T) sThreadLocal; 32 static T sGlobal; 33 static bool sIsTlsUsed; 34 35 // In normal cases, TLS is always available and the class uses sThreadLocal 36 // without changing sMainThreadId. So sMainThreadId is likely to be 0. 37 // 38 // If TLS is not available, we use sGlobal instead and update sMainThreadId 39 // so that that thread keeps using sGlobal even after TLS is initialized 40 // later. 41 static DWORD sMainThreadId; 42 43 // Need non-inline accessors to prevent the compiler from generating code 44 // accessing sThreadLocal before checking a condition. 45 MOZ_NEVER_INLINE static void SetGlobalValue(T aValue) { sGlobal = aValue; } 46 MOZ_NEVER_INLINE static T GetGlobalValue() { return sGlobal; } 47 48 public: 49 static void set(T aValue) { 50 static_assert(std::is_pointer_v<T>, 51 "SafeThreadLocal must be used with a pointer"); 52 53 if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) { 54 SetGlobalValue(aValue); 55 } else if (sIsTlsUsed) { 56 MOZ_ASSERT(mozilla::nt::RtlGetThreadLocalStoragePointer(), 57 "Once TLS is used, TLS should be available till the end."); 58 sThreadLocal.set(aValue); 59 } else if (mozilla::nt::RtlGetThreadLocalStoragePointer()) { 60 sIsTlsUsed = true; 61 sThreadLocal.set(aValue); 62 } else { 63 MOZ_ASSERT(sMainThreadId == 0, 64 "A second thread cannot be created before TLS is available."); 65 sMainThreadId = mozilla::nt::RtlGetCurrentThreadId(); 66 SetGlobalValue(aValue); 67 } 68 } 69 70 static T get() { 71 if (sMainThreadId == mozilla::nt::RtlGetCurrentThreadId()) { 72 return GetGlobalValue(); 73 } else if (sIsTlsUsed) { 74 return sThreadLocal.get(); 75 } 76 return GetGlobalValue(); 77 } 78 }; 79 80 template <typename T> 81 MOZ_GLOBINIT MOZ_THREAD_LOCAL(T) SafeThreadLocal<T>::sThreadLocal; 82 83 template <typename T> 84 T SafeThreadLocal<T>::sGlobal = nullptr; 85 86 template <typename T> 87 bool SafeThreadLocal<T>::sIsTlsUsed = false; 88 89 template <typename T> 90 DWORD SafeThreadLocal<T>::sMainThreadId = 0; 91 92 } // namespace freestanding 93 } // namespace mozilla 94 95 #endif // mozilla_freestanding_SafeThreadLocal_h