1 //===-- scudo_tsd_shared.cpp ------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// Scudo shared TSD implementation. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "scudo_tsd.h" 15 16 #if !SCUDO_TSD_EXCLUSIVE 17 18 namespace __scudo { 19 20 static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT; 21 pthread_key_t PThreadKey; 22 23 static atomic_uint32_t CurrentIndex; 24 static ScudoTSD *TSDs; 25 static u32 NumberOfTSDs; 26 27 // sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used as they allocate memory. 28 static uptr getNumberOfCPUs() { 29 cpu_set_t CPUs; 30 CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0); 31 return CPU_COUNT(&CPUs); 32 } 33 34 static void initOnce() { 35 CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0); 36 initScudo(); 37 NumberOfTSDs = getNumberOfCPUs(); 38 if (NumberOfTSDs == 0) 39 NumberOfTSDs = 1; 40 if (NumberOfTSDs > 32) 41 NumberOfTSDs = 32; 42 TSDs = reinterpret_cast<ScudoTSD *>( 43 MmapOrDie(sizeof(ScudoTSD) * NumberOfTSDs, "ScudoTSDs")); 44 for (u32 i = 0; i < NumberOfTSDs; i++) 45 TSDs[i].init(/*Shared=*/true); 46 } 47 48 ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) { 49 #if SANITIZER_ANDROID 50 *get_android_tls_ptr() = reinterpret_cast<uptr>(TSD); 51 #else 52 CHECK_EQ(pthread_setspecific(PThreadKey, reinterpret_cast<void *>(TSD)), 0); 53 #endif // SANITIZER_ANDROID 54 } 55 56 void initThread(bool MinimalInit) { 57 pthread_once(&GlobalInitialized, initOnce); 58 // Initial context assignment is done in a plain round-robin fashion. 59 u32 Index = atomic_fetch_add(&CurrentIndex, 1, memory_order_relaxed); 60 setCurrentTSD(&TSDs[Index % NumberOfTSDs]); 61 } 62 63 ScudoTSD *getTSDAndLockSlow() { 64 ScudoTSD *TSD; 65 if (NumberOfTSDs > 1) { 66 // Go through all the contexts and find the first unlocked one. 67 for (u32 i = 0; i < NumberOfTSDs; i++) { 68 TSD = &TSDs[i]; 69 if (TSD->tryLock()) { 70 setCurrentTSD(TSD); 71 return TSD; 72 } 73 } 74 // No luck, find the one with the lowest Precedence, and slow lock it. 75 u64 LowestPrecedence = UINT64_MAX; 76 for (u32 i = 0; i < NumberOfTSDs; i++) { 77 u64 Precedence = TSDs[i].getPrecedence(); 78 if (Precedence && Precedence < LowestPrecedence) { 79 TSD = &TSDs[i]; 80 LowestPrecedence = Precedence; 81 } 82 } 83 if (LIKELY(LowestPrecedence != UINT64_MAX)) { 84 TSD->lock(); 85 setCurrentTSD(TSD); 86 return TSD; 87 } 88 } 89 // Last resort, stick with the current one. 90 TSD = getCurrentTSD(); 91 TSD->lock(); 92 return TSD; 93 } 94 95 } // namespace __scudo 96 97 #endif // !SCUDO_TSD_EXCLUSIVE 98