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 static 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   // Hack: TLS_SLOT_TSAN was introduced in N. To be able to use it on M for
36   // testing, we create an unused key. Since the key_data array follows the tls
37   // array, it basically gives us the extra entry we need.
38   // TODO(kostyak): remove and restrict to N and above.
39   CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0);
40   initScudo();
41   NumberOfTSDs = getNumberOfCPUs();
42   if (NumberOfTSDs == 0)
43     NumberOfTSDs = 1;
44   if (NumberOfTSDs > 32)
45     NumberOfTSDs = 32;
46   TSDs = reinterpret_cast<ScudoTSD *>(
47       MmapOrDie(sizeof(ScudoTSD) * NumberOfTSDs, "ScudoTSDs"));
48   for (u32 i = 0; i < NumberOfTSDs; i++)
49     TSDs[i].init(/*Shared=*/true);
50 }
51 
52 ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) {
53   *get_android_tls_ptr() = reinterpret_cast<uptr>(TSD);
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