xref: /aosp_15_r20/external/scudo/standalone/tsd_exclusive.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
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