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#ifndef _WIN32
18#include <pthread.h>
19#include <signal.h>
20#endif
21
22#include <atomic>
23#include <cassert>
24#include <memory>
25
26#include "absl/base/call_once.h"
27#include "absl/base/internal/raw_logging.h"
28#include "absl/base/internal/spinlock.h"
29
30namespace absl {
31namespace base_internal {
32
33#if ABSL_THREAD_IDENTITY_MODE != ABSL_THREAD_IDENTITY_MODE_USE_CPP11
34namespace {
35// Used to co-ordinate one-time creation of our pthread_key
36absl::once_flag init_thread_identity_key_once;
37pthread_key_t thread_identity_pthread_key;
38std::atomic<bool> pthread_key_initialized(false);
39
40void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) {
41 pthread_key_create(&thread_identity_pthread_key, reclaimer);
42 pthread_key_initialized.store(true, std::memory_order_release);
43}
44} // namespace
45#endif
46
47#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
48 ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
49// The actual TLS storage for a thread's currently associated ThreadIdentity.
50// This is referenced by inline accessors in the header.
51// "protected" visibility ensures that if multiple instances of Abseil code
52// exist within a process (via dlopen() or similar), references to
53// thread_identity_ptr from each instance of the code will refer to
54// *different* instances of this ptr.
55#ifdef __GNUC__
56__attribute__((visibility("protected")))
57#endif // __GNUC__
58 ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr;
59#endif // TLS or CPP11
60
61void SetCurrentThreadIdentity(
62 ThreadIdentity* identity, ThreadIdentityReclaimerFunction reclaimer) {
63 assert(CurrentThreadIdentityIfPresent() == nullptr);
64 // Associate our destructor.
65 // NOTE: This call to pthread_setspecific is currently the only immovable
66 // barrier to CurrentThreadIdentity() always being async signal safe.
67#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
68 // NOTE: Not async-safe. But can be open-coded.
69 absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
70 reclaimer);
71
72#ifdef __EMSCRIPTEN__
73 // Emscripten PThread implementation does not support signals.
74 // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html
75 // for more information.
76 pthread_setspecific(thread_identity_pthread_key,
77 reinterpret_cast<void*>(identity));
78#else
79 // We must mask signals around the call to setspecific as with current glibc,
80 // a concurrent getspecific (needed for GetCurrentThreadIdentityIfPresent())
81 // may zero our value.
82 //
83 // While not officially async-signal safe, getspecific within a signal handler
84 // is otherwise OK.
85 sigset_t all_signals;
86 sigset_t curr_signals;
87 sigfillset(&all_signals);
88 pthread_sigmask(SIG_SETMASK, &all_signals, &curr_signals);
89 pthread_setspecific(thread_identity_pthread_key,
90 reinterpret_cast<void*>(identity));
91 pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr);
92#endif // !__EMSCRIPTEN__
93
94#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS
95 // NOTE: Not async-safe. But can be open-coded.
96 absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey,
97 reclaimer);
98 pthread_setspecific(thread_identity_pthread_key,
99 reinterpret_cast<void*>(identity));
100 thread_identity_ptr = identity;
101#elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
102 thread_local std::unique_ptr<ThreadIdentity, ThreadIdentityReclaimerFunction>
103 holder(identity, reclaimer);
104 thread_identity_ptr = identity;
105#else
106#error Unimplemented ABSL_THREAD_IDENTITY_MODE
107#endif
108}
109
110void ClearCurrentThreadIdentity() {
111#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \
112 ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11
113 thread_identity_ptr = nullptr;
114#elif ABSL_THREAD_IDENTITY_MODE == \
115 ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
116 // pthread_setspecific expected to clear value on destruction
117 assert(CurrentThreadIdentityIfPresent() == nullptr);
118#endif
119}
120
121#if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_POSIX_SETSPECIFIC
122ThreadIdentity* CurrentThreadIdentityIfPresent() {
123 bool initialized = pthread_key_initialized.load(std::memory_order_acquire);
124 if (!initialized) {
125 return nullptr;
126 }
127 return reinterpret_cast<ThreadIdentity*>(
128 pthread_getspecific(thread_identity_pthread_key));
129}
130#endif
131
132} // namespace base_internal
133} // namespace absl
134