/src/abseil-cpp/absl/base/internal/thread_identity.cc
Line | Count | Source (jump to first uncovered line) |
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) || defined(__MINGW32__) |
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 | 2 | void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { |
47 | 2 | pthread_key_create(&thread_identity_pthread_key, reclaimer); |
48 | 2 | pthread_key_initialized.store(true, std::memory_order_release); |
49 | 2 | } |
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 | 2 | ThreadIdentityReclaimerFunction reclaimer) { |
78 | 2 | 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 | 0 | #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC |
83 | | // NOTE: Not async-safe. But can be open-coded. |
84 | 0 | absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, |
85 | 2 | 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 | 2 | sigset_t all_signals; |
103 | 2 | sigset_t curr_signals; |
104 | 2 | sigfillset(&all_signals); |
105 | 2 | pthread_sigmask(SIG_SETMASK, &all_signals, &curr_signals); |
106 | 2 | pthread_setspecific(thread_identity_pthread_key, |
107 | 2 | reinterpret_cast<void*>(identity)); |
108 | 2 | pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); |
109 | 2 | #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 | 2 | } |
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 | 0 | 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 | 0 | assert(CurrentThreadIdentityIfPresent() == nullptr); |
147 | 0 | #endif |
148 | 0 | } |
149 | | |
150 | | #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC |
151 | 9.72M | ThreadIdentity* CurrentThreadIdentityIfPresent() { |
152 | 9.72M | bool initialized = pthread_key_initialized.load(std::memory_order_acquire); |
153 | 9.72M | if (!initialized) { |
154 | 4 | return nullptr; |
155 | 4 | } |
156 | 9.72M | return reinterpret_cast<ThreadIdentity*>( |
157 | 9.72M | pthread_getspecific(thread_identity_pthread_key)); |
158 | 9.72M | } |
159 | | #endif |
160 | | |
161 | | } // namespace base_internal |
162 | | ABSL_NAMESPACE_END |
163 | | } // namespace absl |