xref: /aosp_15_r20/external/scudo/standalone/tsd_shared.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
1*76559068SAndroid Build Coastguard Worker //===-- tsd_shared.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_SHARED_H_
10*76559068SAndroid Build Coastguard Worker #define SCUDO_TSD_SHARED_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 #if SCUDO_HAS_PLATFORM_TLS_SLOT
17*76559068SAndroid Build Coastguard Worker // This is a platform-provided header that needs to be on the include path when
18*76559068SAndroid Build Coastguard Worker // Scudo is compiled. It must declare a function with the prototype:
19*76559068SAndroid Build Coastguard Worker //   uintptr_t *getPlatformAllocatorTlsSlot()
20*76559068SAndroid Build Coastguard Worker // that returns the address of a thread-local word of storage reserved for
21*76559068SAndroid Build Coastguard Worker // Scudo, that must be zero-initialized in newly created threads.
22*76559068SAndroid Build Coastguard Worker #include "scudo_platform_tls_slot.h"
23*76559068SAndroid Build Coastguard Worker #endif
24*76559068SAndroid Build Coastguard Worker 
25*76559068SAndroid Build Coastguard Worker namespace scudo {
26*76559068SAndroid Build Coastguard Worker 
27*76559068SAndroid Build Coastguard Worker template <class Allocator, u32 TSDsArraySize, u32 DefaultTSDCount>
28*76559068SAndroid Build Coastguard Worker struct TSDRegistrySharedT {
29*76559068SAndroid Build Coastguard Worker   using ThisT = TSDRegistrySharedT<Allocator, TSDsArraySize, DefaultTSDCount>;
30*76559068SAndroid Build Coastguard Worker 
31*76559068SAndroid Build Coastguard Worker   struct ScopedTSD {
ScopedTSDTSDRegistrySharedT::ScopedTSD32*76559068SAndroid Build Coastguard Worker     ALWAYS_INLINE ScopedTSD(ThisT &TSDRegistry) {
33*76559068SAndroid Build Coastguard Worker       CurrentTSD = TSDRegistry.getTSDAndLock();
34*76559068SAndroid Build Coastguard Worker       DCHECK_NE(CurrentTSD, nullptr);
35*76559068SAndroid Build Coastguard Worker     }
36*76559068SAndroid Build Coastguard Worker 
~ScopedTSDTSDRegistrySharedT::ScopedTSD37*76559068SAndroid Build Coastguard Worker     ~ScopedTSD() { CurrentTSD->unlock(); }
38*76559068SAndroid Build Coastguard Worker 
39*76559068SAndroid Build Coastguard Worker     TSD<Allocator> &operator*() { return *CurrentTSD; }
40*76559068SAndroid Build Coastguard Worker 
41*76559068SAndroid Build Coastguard Worker     TSD<Allocator> *operator->() {
42*76559068SAndroid Build Coastguard Worker       CurrentTSD->assertLocked(/*BypassCheck=*/false);
43*76559068SAndroid Build Coastguard Worker       return CurrentTSD;
44*76559068SAndroid Build Coastguard Worker     }
45*76559068SAndroid Build Coastguard Worker 
46*76559068SAndroid Build Coastguard Worker   private:
47*76559068SAndroid Build Coastguard Worker     TSD<Allocator> *CurrentTSD;
48*76559068SAndroid Build Coastguard Worker   };
49*76559068SAndroid Build Coastguard Worker 
initTSDRegistrySharedT50*76559068SAndroid Build Coastguard Worker   void init(Allocator *Instance) REQUIRES(Mutex) {
51*76559068SAndroid Build Coastguard Worker     DCHECK(!Initialized);
52*76559068SAndroid Build Coastguard Worker     Instance->init();
53*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < TSDsArraySize; I++)
54*76559068SAndroid Build Coastguard Worker       TSDs[I].init(Instance);
55*76559068SAndroid Build Coastguard Worker     const u32 NumberOfCPUs = getNumberOfCPUs();
56*76559068SAndroid Build Coastguard Worker     setNumberOfTSDs((NumberOfCPUs == 0) ? DefaultTSDCount
57*76559068SAndroid Build Coastguard Worker                                         : Min(NumberOfCPUs, DefaultTSDCount));
58*76559068SAndroid Build Coastguard Worker     Initialized = true;
59*76559068SAndroid Build Coastguard Worker   }
60*76559068SAndroid Build Coastguard Worker 
initOnceMaybeTSDRegistrySharedT61*76559068SAndroid Build Coastguard Worker   void initOnceMaybe(Allocator *Instance) EXCLUDES(Mutex) {
62*76559068SAndroid Build Coastguard Worker     ScopedLock L(Mutex);
63*76559068SAndroid Build Coastguard Worker     if (LIKELY(Initialized))
64*76559068SAndroid Build Coastguard Worker       return;
65*76559068SAndroid Build Coastguard Worker     init(Instance); // Sets Initialized.
66*76559068SAndroid Build Coastguard Worker   }
67*76559068SAndroid Build Coastguard Worker 
unmapTestOnlyTSDRegistrySharedT68*76559068SAndroid Build Coastguard Worker   void unmapTestOnly(Allocator *Instance) EXCLUDES(Mutex) {
69*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < TSDsArraySize; I++) {
70*76559068SAndroid Build Coastguard Worker       TSDs[I].commitBack(Instance);
71*76559068SAndroid Build Coastguard Worker       TSDs[I] = {};
72*76559068SAndroid Build Coastguard Worker     }
73*76559068SAndroid Build Coastguard Worker     setCurrentTSD(nullptr);
74*76559068SAndroid Build Coastguard Worker     ScopedLock L(Mutex);
75*76559068SAndroid Build Coastguard Worker     Initialized = false;
76*76559068SAndroid Build Coastguard Worker   }
77*76559068SAndroid Build Coastguard Worker 
drainCachesTSDRegistrySharedT78*76559068SAndroid Build Coastguard Worker   void drainCaches(Allocator *Instance) {
79*76559068SAndroid Build Coastguard Worker     ScopedLock L(MutexTSDs);
80*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumberOfTSDs; ++I) {
81*76559068SAndroid Build Coastguard Worker       TSDs[I].lock();
82*76559068SAndroid Build Coastguard Worker       Instance->drainCache(&TSDs[I]);
83*76559068SAndroid Build Coastguard Worker       TSDs[I].unlock();
84*76559068SAndroid Build Coastguard Worker     }
85*76559068SAndroid Build Coastguard Worker   }
86*76559068SAndroid Build Coastguard Worker 
initThreadMaybeTSDRegistrySharedT87*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE void initThreadMaybe(Allocator *Instance,
88*76559068SAndroid Build Coastguard Worker                                      UNUSED bool MinimalInit) {
89*76559068SAndroid Build Coastguard Worker     if (LIKELY(getCurrentTSD()))
90*76559068SAndroid Build Coastguard Worker       return;
91*76559068SAndroid Build Coastguard Worker     initThread(Instance);
92*76559068SAndroid Build Coastguard Worker   }
93*76559068SAndroid Build Coastguard Worker 
disableTSDRegistrySharedT94*76559068SAndroid Build Coastguard Worker   void disable() NO_THREAD_SAFETY_ANALYSIS {
95*76559068SAndroid Build Coastguard Worker     Mutex.lock();
96*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < TSDsArraySize; I++)
97*76559068SAndroid Build Coastguard Worker       TSDs[I].lock();
98*76559068SAndroid Build Coastguard Worker   }
99*76559068SAndroid Build Coastguard Worker 
enableTSDRegistrySharedT100*76559068SAndroid Build Coastguard Worker   void enable() NO_THREAD_SAFETY_ANALYSIS {
101*76559068SAndroid Build Coastguard Worker     for (s32 I = static_cast<s32>(TSDsArraySize - 1); I >= 0; I--)
102*76559068SAndroid Build Coastguard Worker       TSDs[I].unlock();
103*76559068SAndroid Build Coastguard Worker     Mutex.unlock();
104*76559068SAndroid Build Coastguard Worker   }
105*76559068SAndroid Build Coastguard Worker 
setOptionTSDRegistrySharedT106*76559068SAndroid Build Coastguard Worker   bool setOption(Option O, sptr Value) {
107*76559068SAndroid Build Coastguard Worker     if (O == Option::MaxTSDsCount)
108*76559068SAndroid Build Coastguard Worker       return setNumberOfTSDs(static_cast<u32>(Value));
109*76559068SAndroid Build Coastguard Worker     if (O == Option::ThreadDisableMemInit)
110*76559068SAndroid Build Coastguard Worker       setDisableMemInit(Value);
111*76559068SAndroid Build Coastguard Worker     // Not supported by the TSD Registry, but not an error either.
112*76559068SAndroid Build Coastguard Worker     return true;
113*76559068SAndroid Build Coastguard Worker   }
114*76559068SAndroid Build Coastguard Worker 
getDisableMemInitTSDRegistrySharedT115*76559068SAndroid Build Coastguard Worker   bool getDisableMemInit() const { return *getTlsPtr() & 1; }
116*76559068SAndroid Build Coastguard Worker 
getStatsTSDRegistrySharedT117*76559068SAndroid Build Coastguard Worker   void getStats(ScopedString *Str) EXCLUDES(MutexTSDs) {
118*76559068SAndroid Build Coastguard Worker     ScopedLock L(MutexTSDs);
119*76559068SAndroid Build Coastguard Worker 
120*76559068SAndroid Build Coastguard Worker     Str->append("Stats: SharedTSDs: %u available; total %u\n", NumberOfTSDs,
121*76559068SAndroid Build Coastguard Worker                 TSDsArraySize);
122*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumberOfTSDs; ++I) {
123*76559068SAndroid Build Coastguard Worker       TSDs[I].lock();
124*76559068SAndroid Build Coastguard Worker       // Theoretically, we want to mark TSD::lock()/TSD::unlock() with proper
125*76559068SAndroid Build Coastguard Worker       // thread annotations. However, given the TSD is only locked on shared
126*76559068SAndroid Build Coastguard Worker       // path, do the assertion in a separate path to avoid confusing the
127*76559068SAndroid Build Coastguard Worker       // analyzer.
128*76559068SAndroid Build Coastguard Worker       TSDs[I].assertLocked(/*BypassCheck=*/true);
129*76559068SAndroid Build Coastguard Worker       Str->append("  Shared TSD[%zu]:\n", I);
130*76559068SAndroid Build Coastguard Worker       TSDs[I].getCache().getStats(Str);
131*76559068SAndroid Build Coastguard Worker       TSDs[I].unlock();
132*76559068SAndroid Build Coastguard Worker     }
133*76559068SAndroid Build Coastguard Worker   }
134*76559068SAndroid Build Coastguard Worker 
135*76559068SAndroid Build Coastguard Worker private:
getTSDAndLockTSDRegistrySharedT136*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE TSD<Allocator> *getTSDAndLock() NO_THREAD_SAFETY_ANALYSIS {
137*76559068SAndroid Build Coastguard Worker     TSD<Allocator> *TSD = getCurrentTSD();
138*76559068SAndroid Build Coastguard Worker     DCHECK(TSD);
139*76559068SAndroid Build Coastguard Worker     // Try to lock the currently associated context.
140*76559068SAndroid Build Coastguard Worker     if (TSD->tryLock())
141*76559068SAndroid Build Coastguard Worker       return TSD;
142*76559068SAndroid Build Coastguard Worker     // If that fails, go down the slow path.
143*76559068SAndroid Build Coastguard Worker     if (TSDsArraySize == 1U) {
144*76559068SAndroid Build Coastguard Worker       // Only 1 TSD, not need to go any further.
145*76559068SAndroid Build Coastguard Worker       // The compiler will optimize this one way or the other.
146*76559068SAndroid Build Coastguard Worker       TSD->lock();
147*76559068SAndroid Build Coastguard Worker       return TSD;
148*76559068SAndroid Build Coastguard Worker     }
149*76559068SAndroid Build Coastguard Worker     return getTSDAndLockSlow(TSD);
150*76559068SAndroid Build Coastguard Worker   }
151*76559068SAndroid Build Coastguard Worker 
getTlsPtrTSDRegistrySharedT152*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE uptr *getTlsPtr() const {
153*76559068SAndroid Build Coastguard Worker #if SCUDO_HAS_PLATFORM_TLS_SLOT
154*76559068SAndroid Build Coastguard Worker     return reinterpret_cast<uptr *>(getPlatformAllocatorTlsSlot());
155*76559068SAndroid Build Coastguard Worker #else
156*76559068SAndroid Build Coastguard Worker     static thread_local uptr ThreadTSD;
157*76559068SAndroid Build Coastguard Worker     return &ThreadTSD;
158*76559068SAndroid Build Coastguard Worker #endif
159*76559068SAndroid Build Coastguard Worker   }
160*76559068SAndroid Build Coastguard Worker 
161*76559068SAndroid Build Coastguard Worker   static_assert(alignof(TSD<Allocator>) >= 2, "");
162*76559068SAndroid Build Coastguard Worker 
setCurrentTSDTSDRegistrySharedT163*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE void setCurrentTSD(TSD<Allocator> *CurrentTSD) {
164*76559068SAndroid Build Coastguard Worker     *getTlsPtr() &= 1;
165*76559068SAndroid Build Coastguard Worker     *getTlsPtr() |= reinterpret_cast<uptr>(CurrentTSD);
166*76559068SAndroid Build Coastguard Worker   }
167*76559068SAndroid Build Coastguard Worker 
getCurrentTSDTSDRegistrySharedT168*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE TSD<Allocator> *getCurrentTSD() {
169*76559068SAndroid Build Coastguard Worker     return reinterpret_cast<TSD<Allocator> *>(*getTlsPtr() & ~1ULL);
170*76559068SAndroid Build Coastguard Worker   }
171*76559068SAndroid Build Coastguard Worker 
setNumberOfTSDsTSDRegistrySharedT172*76559068SAndroid Build Coastguard Worker   bool setNumberOfTSDs(u32 N) EXCLUDES(MutexTSDs) {
173*76559068SAndroid Build Coastguard Worker     ScopedLock L(MutexTSDs);
174*76559068SAndroid Build Coastguard Worker     if (N < NumberOfTSDs)
175*76559068SAndroid Build Coastguard Worker       return false;
176*76559068SAndroid Build Coastguard Worker     if (N > TSDsArraySize)
177*76559068SAndroid Build Coastguard Worker       N = TSDsArraySize;
178*76559068SAndroid Build Coastguard Worker     NumberOfTSDs = N;
179*76559068SAndroid Build Coastguard Worker     NumberOfCoPrimes = 0;
180*76559068SAndroid Build Coastguard Worker     // Compute all the coprimes of NumberOfTSDs. This will be used to walk the
181*76559068SAndroid Build Coastguard Worker     // array of TSDs in a random order. For details, see:
182*76559068SAndroid Build Coastguard Worker     // https://lemire.me/blog/2017/09/18/visiting-all-values-in-an-array-exactly-once-in-random-order/
183*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < N; I++) {
184*76559068SAndroid Build Coastguard Worker       u32 A = I + 1;
185*76559068SAndroid Build Coastguard Worker       u32 B = N;
186*76559068SAndroid Build Coastguard Worker       // Find the GCD between I + 1 and N. If 1, they are coprimes.
187*76559068SAndroid Build Coastguard Worker       while (B != 0) {
188*76559068SAndroid Build Coastguard Worker         const u32 T = A;
189*76559068SAndroid Build Coastguard Worker         A = B;
190*76559068SAndroid Build Coastguard Worker         B = T % B;
191*76559068SAndroid Build Coastguard Worker       }
192*76559068SAndroid Build Coastguard Worker       if (A == 1)
193*76559068SAndroid Build Coastguard Worker         CoPrimes[NumberOfCoPrimes++] = I + 1;
194*76559068SAndroid Build Coastguard Worker     }
195*76559068SAndroid Build Coastguard Worker     return true;
196*76559068SAndroid Build Coastguard Worker   }
197*76559068SAndroid Build Coastguard Worker 
setDisableMemInitTSDRegistrySharedT198*76559068SAndroid Build Coastguard Worker   void setDisableMemInit(bool B) {
199*76559068SAndroid Build Coastguard Worker     *getTlsPtr() &= ~1ULL;
200*76559068SAndroid Build Coastguard Worker     *getTlsPtr() |= B;
201*76559068SAndroid Build Coastguard Worker   }
202*76559068SAndroid Build Coastguard Worker 
initThreadTSDRegistrySharedT203*76559068SAndroid Build Coastguard Worker   NOINLINE void initThread(Allocator *Instance) NO_THREAD_SAFETY_ANALYSIS {
204*76559068SAndroid Build Coastguard Worker     initOnceMaybe(Instance);
205*76559068SAndroid Build Coastguard Worker     // Initial context assignment is done in a plain round-robin fashion.
206*76559068SAndroid Build Coastguard Worker     const u32 Index = atomic_fetch_add(&CurrentIndex, 1U, memory_order_relaxed);
207*76559068SAndroid Build Coastguard Worker     setCurrentTSD(&TSDs[Index % NumberOfTSDs]);
208*76559068SAndroid Build Coastguard Worker     Instance->callPostInitCallback();
209*76559068SAndroid Build Coastguard Worker   }
210*76559068SAndroid Build Coastguard Worker 
211*76559068SAndroid Build Coastguard Worker   // TSDs is an array of locks which is not supported for marking thread-safety
212*76559068SAndroid Build Coastguard Worker   // capability.
getTSDAndLockSlowTSDRegistrySharedT213*76559068SAndroid Build Coastguard Worker   NOINLINE TSD<Allocator> *getTSDAndLockSlow(TSD<Allocator> *CurrentTSD)
214*76559068SAndroid Build Coastguard Worker       EXCLUDES(MutexTSDs) {
215*76559068SAndroid Build Coastguard Worker     // Use the Precedence of the current TSD as our random seed. Since we are
216*76559068SAndroid Build Coastguard Worker     // in the slow path, it means that tryLock failed, and as a result it's
217*76559068SAndroid Build Coastguard Worker     // very likely that said Precedence is non-zero.
218*76559068SAndroid Build Coastguard Worker     const u32 R = static_cast<u32>(CurrentTSD->getPrecedence());
219*76559068SAndroid Build Coastguard Worker     u32 N, Inc;
220*76559068SAndroid Build Coastguard Worker     {
221*76559068SAndroid Build Coastguard Worker       ScopedLock L(MutexTSDs);
222*76559068SAndroid Build Coastguard Worker       N = NumberOfTSDs;
223*76559068SAndroid Build Coastguard Worker       DCHECK_NE(NumberOfCoPrimes, 0U);
224*76559068SAndroid Build Coastguard Worker       Inc = CoPrimes[R % NumberOfCoPrimes];
225*76559068SAndroid Build Coastguard Worker     }
226*76559068SAndroid Build Coastguard Worker     if (N > 1U) {
227*76559068SAndroid Build Coastguard Worker       u32 Index = R % N;
228*76559068SAndroid Build Coastguard Worker       uptr LowestPrecedence = UINTPTR_MAX;
229*76559068SAndroid Build Coastguard Worker       TSD<Allocator> *CandidateTSD = nullptr;
230*76559068SAndroid Build Coastguard Worker       // Go randomly through at most 4 contexts and find a candidate.
231*76559068SAndroid Build Coastguard Worker       for (u32 I = 0; I < Min(4U, N); I++) {
232*76559068SAndroid Build Coastguard Worker         if (TSDs[Index].tryLock()) {
233*76559068SAndroid Build Coastguard Worker           setCurrentTSD(&TSDs[Index]);
234*76559068SAndroid Build Coastguard Worker           return &TSDs[Index];
235*76559068SAndroid Build Coastguard Worker         }
236*76559068SAndroid Build Coastguard Worker         const uptr Precedence = TSDs[Index].getPrecedence();
237*76559068SAndroid Build Coastguard Worker         // A 0 precedence here means another thread just locked this TSD.
238*76559068SAndroid Build Coastguard Worker         if (Precedence && Precedence < LowestPrecedence) {
239*76559068SAndroid Build Coastguard Worker           CandidateTSD = &TSDs[Index];
240*76559068SAndroid Build Coastguard Worker           LowestPrecedence = Precedence;
241*76559068SAndroid Build Coastguard Worker         }
242*76559068SAndroid Build Coastguard Worker         Index += Inc;
243*76559068SAndroid Build Coastguard Worker         if (Index >= N)
244*76559068SAndroid Build Coastguard Worker           Index -= N;
245*76559068SAndroid Build Coastguard Worker       }
246*76559068SAndroid Build Coastguard Worker       if (CandidateTSD) {
247*76559068SAndroid Build Coastguard Worker         CandidateTSD->lock();
248*76559068SAndroid Build Coastguard Worker         setCurrentTSD(CandidateTSD);
249*76559068SAndroid Build Coastguard Worker         return CandidateTSD;
250*76559068SAndroid Build Coastguard Worker       }
251*76559068SAndroid Build Coastguard Worker     }
252*76559068SAndroid Build Coastguard Worker     // Last resort, stick with the current one.
253*76559068SAndroid Build Coastguard Worker     CurrentTSD->lock();
254*76559068SAndroid Build Coastguard Worker     return CurrentTSD;
255*76559068SAndroid Build Coastguard Worker   }
256*76559068SAndroid Build Coastguard Worker 
257*76559068SAndroid Build Coastguard Worker   atomic_u32 CurrentIndex = {};
258*76559068SAndroid Build Coastguard Worker   u32 NumberOfTSDs GUARDED_BY(MutexTSDs) = 0;
259*76559068SAndroid Build Coastguard Worker   u32 NumberOfCoPrimes GUARDED_BY(MutexTSDs) = 0;
260*76559068SAndroid Build Coastguard Worker   u32 CoPrimes[TSDsArraySize] GUARDED_BY(MutexTSDs) = {};
261*76559068SAndroid Build Coastguard Worker   bool Initialized GUARDED_BY(Mutex) = false;
262*76559068SAndroid Build Coastguard Worker   HybridMutex Mutex;
263*76559068SAndroid Build Coastguard Worker   HybridMutex MutexTSDs;
264*76559068SAndroid Build Coastguard Worker   TSD<Allocator> TSDs[TSDsArraySize];
265*76559068SAndroid Build Coastguard Worker };
266*76559068SAndroid Build Coastguard Worker 
267*76559068SAndroid Build Coastguard Worker } // namespace scudo
268*76559068SAndroid Build Coastguard Worker 
269*76559068SAndroid Build Coastguard Worker #endif // SCUDO_TSD_SHARED_H_
270