thread_identity.cc (6562B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/base/internal/thread_identity.h" 16 17 #if !defined(_WIN32) 18 #include <pthread.h> 19 #ifndef __wasi__ 20 // WASI does not provide this header, either way we disable use 21 // of signals with it below. 22 #include <signal.h> 23 #endif 24 #endif 25 26 #include <atomic> 27 #include <cassert> 28 #include <memory> 29 30 #include "absl/base/attributes.h" 31 #include "absl/base/call_once.h" 32 #include "absl/base/internal/raw_logging.h" 33 #include "absl/base/internal/spinlock.h" 34 35 namespace absl { 36 ABSL_NAMESPACE_BEGIN 37 namespace base_internal { 38 39 #if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11 40 namespace { 41 // Used to co-ordinate one-time creation of our pthread_key 42 absl::once_flag init_thread_identity_key_once; 43 pthread_key_t thread_identity_pthread_key; 44 std::atomic<bool> pthread_key_initialized(false); 45 46 void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { 47 pthread_key_create(&thread_identity_pthread_key, reclaimer); 48 pthread_key_initialized.store(true, std::memory_order_release); 49 } 50 } // namespace 51 #endif 52 53 #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ 54 ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 55 // The actual TLS storage for a thread's currently associated ThreadIdentity. 56 // This is referenced by inline accessors in the header. 57 // "protected" visibility ensures that if multiple instances of Abseil code 58 // exist within a process (via dlopen() or similar), references to 59 // thread_identity_ptr from each instance of the code will refer to 60 // *different* instances of this ptr. 61 // Apple platforms have the visibility attribute, but issue a compile warning 62 // that protected visibility is unsupported. 63 ABSL_CONST_INIT // Must come before __attribute__((visibility("protected"))) 64 #if ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) 65 __attribute__((visibility("protected"))) 66 #endif // ABSL_HAVE_ATTRIBUTE(visibility) && !defined(__APPLE__) 67 #if ABSL_PER_THREAD_TLS 68 // Prefer __thread to thread_local as benchmarks indicate it is a bit 69 // faster. 70 ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr; 71 #elif defined(ABSL_HAVE_THREAD_LOCAL) 72 thread_local ThreadIdentity* thread_identity_ptr = nullptr; 73 #endif // ABSL_PER_THREAD_TLS 74 #endif // TLS or CPP11 75 76 void SetCurrentThreadIdentity(ThreadIdentity* identity, 77 ThreadIdentityReclaimerFunction reclaimer) { 78 assert(CurrentThreadIdentityIfPresent() == nullptr); 79 // Associate our destructor. 80 // NOTE: This call to pthread_setspecific is currently the only immovable 81 // barrier to CurrentThreadIdentity() always being async signal safe. 82 #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 83 // NOTE: Not async-safe. But can be open-coded. 84 absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, 85 reclaimer); 86 87 #if defined(__wasi__) || defined(__EMSCRIPTEN__) || defined(__MINGW32__) || \ 88 defined(__hexagon__) 89 // Emscripten, WASI and MinGW pthread implementations does not support 90 // signals. See 91 // https://kripken.github.io/emscripten-site/docs/porting/pthreads.html for 92 // more information. 93 pthread_setspecific(thread_identity_pthread_key, 94 reinterpret_cast<void*>(identity)); 95 #else 96 // We must mask signals around the call to setspecific as with current glibc, 97 // a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent()) 98 // may zero our value. 99 // 100 // While not officially async-signal safe, getspecific within a signal handler 101 // is otherwise OK. 102 sigset_t all_signals; 103 sigset_t curr_signals; 104 sigfillset(&all_signals); 105 pthread_sigmask(SIG_SETMASK, &all_signals, &curr_signals); 106 pthread_setspecific(thread_identity_pthread_key, 107 reinterpret_cast<void*>(identity)); 108 pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); 109 #endif // !__EMSCRIPTEN__ && !__MINGW32__ 110 111 #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS 112 // NOTE: Not async-safe. But can be open-coded. 113 absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, 114 reclaimer); 115 pthread_setspecific(thread_identity_pthread_key, 116 reinterpret_cast<void*>(identity)); 117 thread_identity_ptr = identity; 118 #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 119 thread_local std::unique_ptr<ThreadIdentity, ThreadIdentityReclaimerFunction> 120 holder(identity, reclaimer); 121 thread_identity_ptr = identity; 122 #else 123 #error Unimplemented ABSL_THREAD_IDENTITY_MODE 124 #endif 125 } 126 127 #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ 128 ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 129 130 // Please see the comment on `CurrentThreadIdentityIfPresent` in 131 // thread_identity.h. When we cannot expose thread_local variables in 132 // headers, we opt for the correct-but-slower option of not inlining this 133 // function. 134 #ifndef ABSL_INTERNAL_INLINE_CURRENT_THREAD_IDENTITY_IF_PRESENT 135 ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; } 136 #endif 137 #endif 138 139 void ClearCurrentThreadIdentity() { 140 #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ 141 ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 142 thread_identity_ptr = nullptr; 143 #elif ABSL_THREAD_IDENTITY_MODE == \ 144 ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 145 // pthread_setspecific expected to clear value on destruction 146 assert(CurrentThreadIdentityIfPresent() == nullptr); 147 #endif 148 } 149 150 #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC 151 ThreadIdentity* CurrentThreadIdentityIfPresent() { 152 bool initialized = pthread_key_initialized.load(std::memory_order_acquire); 153 if (!initialized) { 154 return nullptr; 155 } 156 return reinterpret_cast<ThreadIdentity*>( 157 pthread_getspecific(thread_identity_pthread_key)); 158 } 159 #endif 160 161 } // namespace base_internal 162 ABSL_NAMESPACE_END 163 } // namespace absl