1 //===-- tsd_shared.h --------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef SCUDO_TSD_SHARED_H_
10 #define SCUDO_TSD_SHARED_H_
12 #include "linux.h" // for getAndroidTlsPtr()
17 template <class Allocator, u32 MaxTSDCount> struct TSDRegistrySharedT {
18 void initLinkerInitialized(Allocator *Instance) {
19 Instance->initLinkerInitialized();
20 CHECK_EQ(pthread_key_create(&PThreadKey, nullptr), 0); // For non-TLS
21 const u32 NumberOfCPUs = getNumberOfCPUs();
22 NumberOfTSDs = (SCUDO_ANDROID || NumberOfCPUs == 0)
24 : Min(NumberOfCPUs, MaxTSDCount);
25 for (u32 I = 0; I < NumberOfTSDs; I++)
26 TSDs[I].initLinkerInitialized(Instance);
27 // Compute all the coprimes of NumberOfTSDs. This will be used to walk the
28 // array of TSDs in a random order. For details, see:
29 // https://lemire.me/blog/2017/09/18/visiting-all-values-in-an-array-exactly-once-in-random-order/
30 for (u32 I = 0; I < NumberOfTSDs; I++) {
33 // Find the GCD between I + 1 and NumberOfTSDs. If 1, they are coprimes.
40 CoPrimes[NumberOfCoPrimes++] = I + 1;
44 void init(Allocator *Instance) {
45 memset(this, 0, sizeof(*this));
46 initLinkerInitialized(Instance);
49 void unmapTestOnly() {
50 setCurrentTSD(nullptr);
51 pthread_key_delete(PThreadKey);
54 ALWAYS_INLINE void initThreadMaybe(Allocator *Instance,
55 UNUSED bool MinimalInit) {
56 if (LIKELY(getCurrentTSD()))
61 ALWAYS_INLINE TSD<Allocator> *getTSDAndLock(bool *UnlockRequired) {
62 TSD<Allocator> *TSD = getCurrentTSD();
64 *UnlockRequired = true;
65 // Try to lock the currently associated context.
68 // If that fails, go down the slow path.
69 return getTSDAndLockSlow(TSD);
74 for (u32 I = 0; I < NumberOfTSDs; I++)
79 for (s32 I = static_cast<s32>(NumberOfTSDs - 1); I >= 0; I--)
85 ALWAYS_INLINE void setCurrentTSD(TSD<Allocator> *CurrentTSD) {
87 *getAndroidTlsPtr() = reinterpret_cast<uptr>(CurrentTSD);
89 ThreadTSD = CurrentTSD;
92 pthread_setspecific(PThreadKey, reinterpret_cast<void *>(CurrentTSD)),
97 ALWAYS_INLINE TSD<Allocator> *getCurrentTSD() {
99 return reinterpret_cast<TSD<Allocator> *>(*getAndroidTlsPtr());
103 return reinterpret_cast<TSD<Allocator> *>(pthread_getspecific(PThreadKey));
107 void initOnceMaybe(Allocator *Instance) {
109 if (LIKELY(Initialized))
111 initLinkerInitialized(Instance); // Sets Initialized.
114 NOINLINE void initThread(Allocator *Instance) {
115 initOnceMaybe(Instance);
116 // Initial context assignment is done in a plain round-robin fashion.
117 const u32 Index = atomic_fetch_add(&CurrentIndex, 1U, memory_order_relaxed);
118 setCurrentTSD(&TSDs[Index % NumberOfTSDs]);
119 Instance->callPostInitCallback();
122 NOINLINE TSD<Allocator> *getTSDAndLockSlow(TSD<Allocator> *CurrentTSD) {
123 if (MaxTSDCount > 1U && NumberOfTSDs > 1U) {
124 // Use the Precedence of the current TSD as our random seed. Since we are
125 // in the slow path, it means that tryLock failed, and as a result it's
126 // very likely that said Precedence is non-zero.
127 const u32 R = static_cast<u32>(CurrentTSD->getPrecedence());
128 const u32 Inc = CoPrimes[R % NumberOfCoPrimes];
129 u32 Index = R % NumberOfTSDs;
130 uptr LowestPrecedence = UINTPTR_MAX;
131 TSD<Allocator> *CandidateTSD = nullptr;
132 // Go randomly through at most 4 contexts and find a candidate.
133 for (u32 I = 0; I < Min(4U, NumberOfTSDs); I++) {
134 if (TSDs[Index].tryLock()) {
135 setCurrentTSD(&TSDs[Index]);
138 const uptr Precedence = TSDs[Index].getPrecedence();
139 // A 0 precedence here means another thread just locked this TSD.
140 if (Precedence && Precedence < LowestPrecedence) {
141 CandidateTSD = &TSDs[Index];
142 LowestPrecedence = Precedence;
145 if (Index >= NumberOfTSDs)
146 Index -= NumberOfTSDs;
149 CandidateTSD->lock();
150 setCurrentTSD(CandidateTSD);
154 // Last resort, stick with the current one.
159 pthread_key_t PThreadKey;
160 atomic_u32 CurrentIndex;
162 u32 NumberOfCoPrimes;
163 u32 CoPrimes[MaxTSDCount];
166 TSD<Allocator> TSDs[MaxTSDCount];
167 #if SCUDO_LINUX && !_BIONIC
168 static THREADLOCAL TSD<Allocator> *ThreadTSD;
172 #if SCUDO_LINUX && !_BIONIC
173 template <class Allocator, u32 MaxTSDCount>
174 THREADLOCAL TSD<Allocator>
175 *TSDRegistrySharedT<Allocator, MaxTSDCount>::ThreadTSD;
180 #endif // SCUDO_TSD_SHARED_H_