1*76559068SAndroid Build Coastguard Worker //===-- tsd_exclusive.h -----------------------------------------*- C++ -*-===//
2*76559068SAndroid Build Coastguard Worker //
3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*76559068SAndroid Build Coastguard Worker //
7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*76559068SAndroid Build Coastguard Worker
9*76559068SAndroid Build Coastguard Worker #ifndef SCUDO_TSD_EXCLUSIVE_H_
10*76559068SAndroid Build Coastguard Worker #define SCUDO_TSD_EXCLUSIVE_H_
11*76559068SAndroid Build Coastguard Worker
12*76559068SAndroid Build Coastguard Worker #include "tsd.h"
13*76559068SAndroid Build Coastguard Worker
14*76559068SAndroid Build Coastguard Worker #include "string_utils.h"
15*76559068SAndroid Build Coastguard Worker
16*76559068SAndroid Build Coastguard Worker namespace scudo {
17*76559068SAndroid Build Coastguard Worker
18*76559068SAndroid Build Coastguard Worker struct ThreadState {
19*76559068SAndroid Build Coastguard Worker bool DisableMemInit : 1;
20*76559068SAndroid Build Coastguard Worker enum : unsigned {
21*76559068SAndroid Build Coastguard Worker NotInitialized = 0,
22*76559068SAndroid Build Coastguard Worker Initialized,
23*76559068SAndroid Build Coastguard Worker TornDown,
24*76559068SAndroid Build Coastguard Worker } InitState : 2;
25*76559068SAndroid Build Coastguard Worker };
26*76559068SAndroid Build Coastguard Worker
27*76559068SAndroid Build Coastguard Worker template <class Allocator> void teardownThread(void *Ptr);
28*76559068SAndroid Build Coastguard Worker
29*76559068SAndroid Build Coastguard Worker template <class Allocator> struct TSDRegistryExT {
30*76559068SAndroid Build Coastguard Worker using ThisT = TSDRegistryExT<Allocator>;
31*76559068SAndroid Build Coastguard Worker
32*76559068SAndroid Build Coastguard Worker struct ScopedTSD {
ScopedTSDTSDRegistryExT::ScopedTSD33*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE ScopedTSD(ThisT &TSDRegistry) {
34*76559068SAndroid Build Coastguard Worker CurrentTSD = TSDRegistry.getTSDAndLock(&UnlockRequired);
35*76559068SAndroid Build Coastguard Worker DCHECK_NE(CurrentTSD, nullptr);
36*76559068SAndroid Build Coastguard Worker }
37*76559068SAndroid Build Coastguard Worker
~ScopedTSDTSDRegistryExT::ScopedTSD38*76559068SAndroid Build Coastguard Worker ~ScopedTSD() {
39*76559068SAndroid Build Coastguard Worker if (UNLIKELY(UnlockRequired))
40*76559068SAndroid Build Coastguard Worker CurrentTSD->unlock();
41*76559068SAndroid Build Coastguard Worker }
42*76559068SAndroid Build Coastguard Worker
43*76559068SAndroid Build Coastguard Worker TSD<Allocator> &operator*() { return *CurrentTSD; }
44*76559068SAndroid Build Coastguard Worker
45*76559068SAndroid Build Coastguard Worker TSD<Allocator> *operator->() {
46*76559068SAndroid Build Coastguard Worker CurrentTSD->assertLocked(/*BypassCheck=*/!UnlockRequired);
47*76559068SAndroid Build Coastguard Worker return CurrentTSD;
48*76559068SAndroid Build Coastguard Worker }
49*76559068SAndroid Build Coastguard Worker
50*76559068SAndroid Build Coastguard Worker private:
51*76559068SAndroid Build Coastguard Worker TSD<Allocator> *CurrentTSD;
52*76559068SAndroid Build Coastguard Worker bool UnlockRequired;
53*76559068SAndroid Build Coastguard Worker };
54*76559068SAndroid Build Coastguard Worker
initTSDRegistryExT55*76559068SAndroid Build Coastguard Worker void init(Allocator *Instance) REQUIRES(Mutex) {
56*76559068SAndroid Build Coastguard Worker DCHECK(!Initialized);
57*76559068SAndroid Build Coastguard Worker Instance->init();
58*76559068SAndroid Build Coastguard Worker CHECK_EQ(pthread_key_create(&PThreadKey, teardownThread<Allocator>), 0);
59*76559068SAndroid Build Coastguard Worker FallbackTSD.init(Instance);
60*76559068SAndroid Build Coastguard Worker Initialized = true;
61*76559068SAndroid Build Coastguard Worker }
62*76559068SAndroid Build Coastguard Worker
initOnceMaybeTSDRegistryExT63*76559068SAndroid Build Coastguard Worker void initOnceMaybe(Allocator *Instance) EXCLUDES(Mutex) {
64*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex);
65*76559068SAndroid Build Coastguard Worker if (LIKELY(Initialized))
66*76559068SAndroid Build Coastguard Worker return;
67*76559068SAndroid Build Coastguard Worker init(Instance); // Sets Initialized.
68*76559068SAndroid Build Coastguard Worker }
69*76559068SAndroid Build Coastguard Worker
unmapTestOnlyTSDRegistryExT70*76559068SAndroid Build Coastguard Worker void unmapTestOnly(Allocator *Instance) EXCLUDES(Mutex) {
71*76559068SAndroid Build Coastguard Worker DCHECK(Instance);
72*76559068SAndroid Build Coastguard Worker if (reinterpret_cast<Allocator *>(pthread_getspecific(PThreadKey))) {
73*76559068SAndroid Build Coastguard Worker DCHECK_EQ(reinterpret_cast<Allocator *>(pthread_getspecific(PThreadKey)),
74*76559068SAndroid Build Coastguard Worker Instance);
75*76559068SAndroid Build Coastguard Worker ThreadTSD.commitBack(Instance);
76*76559068SAndroid Build Coastguard Worker ThreadTSD = {};
77*76559068SAndroid Build Coastguard Worker }
78*76559068SAndroid Build Coastguard Worker CHECK_EQ(pthread_key_delete(PThreadKey), 0);
79*76559068SAndroid Build Coastguard Worker PThreadKey = {};
80*76559068SAndroid Build Coastguard Worker FallbackTSD.commitBack(Instance);
81*76559068SAndroid Build Coastguard Worker FallbackTSD = {};
82*76559068SAndroid Build Coastguard Worker State = {};
83*76559068SAndroid Build Coastguard Worker ScopedLock L(Mutex);
84*76559068SAndroid Build Coastguard Worker Initialized = false;
85*76559068SAndroid Build Coastguard Worker }
86*76559068SAndroid Build Coastguard Worker
drainCachesTSDRegistryExT87*76559068SAndroid Build Coastguard Worker void drainCaches(Allocator *Instance) {
88*76559068SAndroid Build Coastguard Worker // We don't have a way to iterate all thread local `ThreadTSD`s. Simply
89*76559068SAndroid Build Coastguard Worker // drain the `ThreadTSD` of current thread and `FallbackTSD`.
90*76559068SAndroid Build Coastguard Worker Instance->drainCache(&ThreadTSD);
91*76559068SAndroid Build Coastguard Worker FallbackTSD.lock();
92*76559068SAndroid Build Coastguard Worker Instance->drainCache(&FallbackTSD);
93*76559068SAndroid Build Coastguard Worker FallbackTSD.unlock();
94*76559068SAndroid Build Coastguard Worker }
95*76559068SAndroid Build Coastguard Worker
initThreadMaybeTSDRegistryExT96*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE void initThreadMaybe(Allocator *Instance, bool MinimalInit) {
97*76559068SAndroid Build Coastguard Worker if (LIKELY(State.InitState != ThreadState::NotInitialized))
98*76559068SAndroid Build Coastguard Worker return;
99*76559068SAndroid Build Coastguard Worker initThread(Instance, MinimalInit);
100*76559068SAndroid Build Coastguard Worker }
101*76559068SAndroid Build Coastguard Worker
102*76559068SAndroid Build Coastguard Worker // To disable the exclusive TSD registry, we effectively lock the fallback TSD
103*76559068SAndroid Build Coastguard Worker // and force all threads to attempt to use it instead of their local one.
disableTSDRegistryExT104*76559068SAndroid Build Coastguard Worker void disable() NO_THREAD_SAFETY_ANALYSIS {
105*76559068SAndroid Build Coastguard Worker Mutex.lock();
106*76559068SAndroid Build Coastguard Worker FallbackTSD.lock();
107*76559068SAndroid Build Coastguard Worker atomic_store(&Disabled, 1U, memory_order_release);
108*76559068SAndroid Build Coastguard Worker }
109*76559068SAndroid Build Coastguard Worker
enableTSDRegistryExT110*76559068SAndroid Build Coastguard Worker void enable() NO_THREAD_SAFETY_ANALYSIS {
111*76559068SAndroid Build Coastguard Worker atomic_store(&Disabled, 0U, memory_order_release);
112*76559068SAndroid Build Coastguard Worker FallbackTSD.unlock();
113*76559068SAndroid Build Coastguard Worker Mutex.unlock();
114*76559068SAndroid Build Coastguard Worker }
115*76559068SAndroid Build Coastguard Worker
setOptionTSDRegistryExT116*76559068SAndroid Build Coastguard Worker bool setOption(Option O, sptr Value) {
117*76559068SAndroid Build Coastguard Worker if (O == Option::ThreadDisableMemInit)
118*76559068SAndroid Build Coastguard Worker State.DisableMemInit = Value;
119*76559068SAndroid Build Coastguard Worker if (O == Option::MaxTSDsCount)
120*76559068SAndroid Build Coastguard Worker return false;
121*76559068SAndroid Build Coastguard Worker return true;
122*76559068SAndroid Build Coastguard Worker }
123*76559068SAndroid Build Coastguard Worker
getDisableMemInitTSDRegistryExT124*76559068SAndroid Build Coastguard Worker bool getDisableMemInit() { return State.DisableMemInit; }
125*76559068SAndroid Build Coastguard Worker
getStatsTSDRegistryExT126*76559068SAndroid Build Coastguard Worker void getStats(ScopedString *Str) {
127*76559068SAndroid Build Coastguard Worker // We don't have a way to iterate all thread local `ThreadTSD`s. Instead of
128*76559068SAndroid Build Coastguard Worker // printing only self `ThreadTSD` which may mislead the usage, we just skip
129*76559068SAndroid Build Coastguard Worker // it.
130*76559068SAndroid Build Coastguard Worker Str->append("Exclusive TSD don't support iterating each TSD\n");
131*76559068SAndroid Build Coastguard Worker }
132*76559068SAndroid Build Coastguard Worker
133*76559068SAndroid Build Coastguard Worker private:
134*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE TSD<Allocator> *
getTSDAndLockTSDRegistryExT135*76559068SAndroid Build Coastguard Worker getTSDAndLock(bool *UnlockRequired) NO_THREAD_SAFETY_ANALYSIS {
136*76559068SAndroid Build Coastguard Worker if (LIKELY(State.InitState == ThreadState::Initialized &&
137*76559068SAndroid Build Coastguard Worker !atomic_load(&Disabled, memory_order_acquire))) {
138*76559068SAndroid Build Coastguard Worker *UnlockRequired = false;
139*76559068SAndroid Build Coastguard Worker return &ThreadTSD;
140*76559068SAndroid Build Coastguard Worker }
141*76559068SAndroid Build Coastguard Worker FallbackTSD.lock();
142*76559068SAndroid Build Coastguard Worker *UnlockRequired = true;
143*76559068SAndroid Build Coastguard Worker return &FallbackTSD;
144*76559068SAndroid Build Coastguard Worker }
145*76559068SAndroid Build Coastguard Worker
146*76559068SAndroid Build Coastguard Worker // Using minimal initialization allows for global initialization while keeping
147*76559068SAndroid Build Coastguard Worker // the thread specific structure untouched. The fallback structure will be
148*76559068SAndroid Build Coastguard Worker // used instead.
initThreadTSDRegistryExT149*76559068SAndroid Build Coastguard Worker NOINLINE void initThread(Allocator *Instance, bool MinimalInit) {
150*76559068SAndroid Build Coastguard Worker initOnceMaybe(Instance);
151*76559068SAndroid Build Coastguard Worker if (UNLIKELY(MinimalInit))
152*76559068SAndroid Build Coastguard Worker return;
153*76559068SAndroid Build Coastguard Worker CHECK_EQ(
154*76559068SAndroid Build Coastguard Worker pthread_setspecific(PThreadKey, reinterpret_cast<void *>(Instance)), 0);
155*76559068SAndroid Build Coastguard Worker ThreadTSD.init(Instance);
156*76559068SAndroid Build Coastguard Worker State.InitState = ThreadState::Initialized;
157*76559068SAndroid Build Coastguard Worker Instance->callPostInitCallback();
158*76559068SAndroid Build Coastguard Worker }
159*76559068SAndroid Build Coastguard Worker
160*76559068SAndroid Build Coastguard Worker pthread_key_t PThreadKey = {};
161*76559068SAndroid Build Coastguard Worker bool Initialized GUARDED_BY(Mutex) = false;
162*76559068SAndroid Build Coastguard Worker atomic_u8 Disabled = {};
163*76559068SAndroid Build Coastguard Worker TSD<Allocator> FallbackTSD;
164*76559068SAndroid Build Coastguard Worker HybridMutex Mutex;
165*76559068SAndroid Build Coastguard Worker static thread_local ThreadState State;
166*76559068SAndroid Build Coastguard Worker static thread_local TSD<Allocator> ThreadTSD;
167*76559068SAndroid Build Coastguard Worker
168*76559068SAndroid Build Coastguard Worker friend void teardownThread<Allocator>(void *Ptr);
169*76559068SAndroid Build Coastguard Worker };
170*76559068SAndroid Build Coastguard Worker
171*76559068SAndroid Build Coastguard Worker template <class Allocator>
172*76559068SAndroid Build Coastguard Worker thread_local TSD<Allocator> TSDRegistryExT<Allocator>::ThreadTSD;
173*76559068SAndroid Build Coastguard Worker template <class Allocator>
174*76559068SAndroid Build Coastguard Worker thread_local ThreadState TSDRegistryExT<Allocator>::State;
175*76559068SAndroid Build Coastguard Worker
176*76559068SAndroid Build Coastguard Worker template <class Allocator>
teardownThread(void * Ptr)177*76559068SAndroid Build Coastguard Worker void teardownThread(void *Ptr) NO_THREAD_SAFETY_ANALYSIS {
178*76559068SAndroid Build Coastguard Worker typedef TSDRegistryExT<Allocator> TSDRegistryT;
179*76559068SAndroid Build Coastguard Worker Allocator *Instance = reinterpret_cast<Allocator *>(Ptr);
180*76559068SAndroid Build Coastguard Worker // The glibc POSIX thread-local-storage deallocation routine calls user
181*76559068SAndroid Build Coastguard Worker // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS.
182*76559068SAndroid Build Coastguard Worker // We want to be called last since other destructors might call free and the
183*76559068SAndroid Build Coastguard Worker // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the
184*76559068SAndroid Build Coastguard Worker // quarantine and swallowing the cache.
185*76559068SAndroid Build Coastguard Worker if (TSDRegistryT::ThreadTSD.DestructorIterations > 1) {
186*76559068SAndroid Build Coastguard Worker TSDRegistryT::ThreadTSD.DestructorIterations--;
187*76559068SAndroid Build Coastguard Worker // If pthread_setspecific fails, we will go ahead with the teardown.
188*76559068SAndroid Build Coastguard Worker if (LIKELY(pthread_setspecific(Instance->getTSDRegistry()->PThreadKey,
189*76559068SAndroid Build Coastguard Worker Ptr) == 0))
190*76559068SAndroid Build Coastguard Worker return;
191*76559068SAndroid Build Coastguard Worker }
192*76559068SAndroid Build Coastguard Worker TSDRegistryT::ThreadTSD.commitBack(Instance);
193*76559068SAndroid Build Coastguard Worker TSDRegistryT::State.InitState = ThreadState::TornDown;
194*76559068SAndroid Build Coastguard Worker }
195*76559068SAndroid Build Coastguard Worker
196*76559068SAndroid Build Coastguard Worker } // namespace scudo
197*76559068SAndroid Build Coastguard Worker
198*76559068SAndroid Build Coastguard Worker #endif // SCUDO_TSD_EXCLUSIVE_H_
199