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