blob: 191c9ff13bbf4ac67f8846cd52eb5214c6597e15 [file] [log] [blame]
//===-- scudo_tsd_shared.cpp ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// Scudo shared TSD implementation.
///
//===----------------------------------------------------------------------===//
#include "scudo_tsd.h"
#if !SCUDO_TSD_EXCLUSIVE
namespace __scudo {
static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT;
pthread_key_t PThreadKey;
static atomic_uint32_t CurrentIndex;
static ScudoTSD *TSDs;
static u32 NumberOfTSDs;
// sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used as they allocate memory.
static u32 getNumberOfCPUs() {
cpu_set_t CPUs;
CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0);
return CPU_COUNT(&CPUs);
}
static void initOnce() {
CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0);
initScudo();
NumberOfTSDs = Min(Max(1U, getNumberOfCPUs()),
static_cast<u32>(SCUDO_SHARED_TSD_POOL_SIZE));
TSDs = reinterpret_cast<ScudoTSD *>(
MmapOrDie(sizeof(ScudoTSD) * NumberOfTSDs, "ScudoTSDs"));
for (u32 i = 0; i < NumberOfTSDs; i++)
TSDs[i].init(/*Shared=*/true);
}
ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) {
#if SANITIZER_ANDROID
*get_android_tls_ptr() = reinterpret_cast<uptr>(TSD);
#else
CHECK_EQ(pthread_setspecific(PThreadKey, reinterpret_cast<void *>(TSD)), 0);
#endif // SANITIZER_ANDROID
}
void initThread(bool MinimalInit) {
pthread_once(&GlobalInitialized, initOnce);
// Initial context assignment is done in a plain round-robin fashion.
u32 Index = atomic_fetch_add(&CurrentIndex, 1, memory_order_relaxed);
setCurrentTSD(&TSDs[Index % NumberOfTSDs]);
}
ScudoTSD *getTSDAndLockSlow() {
ScudoTSD *TSD;
if (NumberOfTSDs > 1) {
// Go through all the contexts and find the first unlocked one.
for (u32 i = 0; i < NumberOfTSDs; i++) {
TSD = &TSDs[i];
if (TSD->tryLock()) {
setCurrentTSD(TSD);
return TSD;
}
}
// No luck, find the one with the lowest Precedence, and slow lock it.
u64 LowestPrecedence = UINT64_MAX;
for (u32 i = 0; i < NumberOfTSDs; i++) {
u64 Precedence = TSDs[i].getPrecedence();
if (Precedence && Precedence < LowestPrecedence) {
TSD = &TSDs[i];
LowestPrecedence = Precedence;
}
}
if (LIKELY(LowestPrecedence != UINT64_MAX)) {
TSD->lock();
setCurrentTSD(TSD);
return TSD;
}
}
// Last resort, stick with the current one.
TSD = getCurrentTSD();
TSD->lock();
return TSD;
}
} // namespace __scudo
#endif // !SCUDO_TSD_EXCLUSIVE