1 //===-- tsd_exclusive.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_EXCLUSIVE_H_
10 #define SCUDO_TSD_EXCLUSIVE_H_
18 enum class ThreadState : u8 {
24 template <class Allocator> void teardownThread(void *Ptr);
26 template <class Allocator> struct TSDRegistryExT {
27 void initLinkerInitialized(Allocator *Instance) {
28 Instance->initLinkerInitialized();
29 CHECK_EQ(pthread_key_create(&PThreadKey, teardownThread<Allocator>), 0);
30 FallbackTSD = reinterpret_cast<TSD<Allocator> *>(
31 map(nullptr, sizeof(TSD<Allocator>), "scudo:tsd"));
32 FallbackTSD->initLinkerInitialized(Instance);
35 void init(Allocator *Instance) {
36 memset(this, 0, sizeof(*this));
37 initLinkerInitialized(Instance);
40 void unmapTestOnly() {
41 unmap(reinterpret_cast<void *>(FallbackTSD), sizeof(TSD<Allocator>));
44 ALWAYS_INLINE void initThreadMaybe(Allocator *Instance, bool MinimalInit) {
45 if (LIKELY(State != ThreadState::NotInitialized))
47 initThread(Instance, MinimalInit);
50 ALWAYS_INLINE TSD<Allocator> *getTSDAndLock(bool *UnlockRequired) {
51 if (LIKELY(State == ThreadState::Initialized)) {
52 *UnlockRequired = false;
57 *UnlockRequired = true;
62 void initOnceMaybe(Allocator *Instance) {
66 initLinkerInitialized(Instance); // Sets Initialized.
69 // Using minimal initialization allows for global initialization while keeping
70 // the thread specific structure untouched. The fallback structure will be
72 NOINLINE void initThread(Allocator *Instance, bool MinimalInit) {
73 initOnceMaybe(Instance);
77 pthread_setspecific(PThreadKey, reinterpret_cast<void *>(Instance)), 0);
78 ThreadTSD.initLinkerInitialized(Instance);
79 State = ThreadState::Initialized;
82 pthread_key_t PThreadKey;
84 TSD<Allocator> *FallbackTSD;
86 static THREADLOCAL ThreadState State;
87 static THREADLOCAL TSD<Allocator> ThreadTSD;
89 friend void teardownThread<Allocator>(void *Ptr);
92 template <class Allocator>
93 THREADLOCAL TSD<Allocator> TSDRegistryExT<Allocator>::ThreadTSD;
94 template <class Allocator>
95 THREADLOCAL ThreadState TSDRegistryExT<Allocator>::State;
97 template <class Allocator> void teardownThread(void *Ptr) {
98 typedef TSDRegistryExT<Allocator> TSDRegistryT;
99 Allocator *Instance = reinterpret_cast<Allocator *>(Ptr);
100 // The glibc POSIX thread-local-storage deallocation routine calls user
101 // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS.
102 // We want to be called last since other destructors might call free and the
103 // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the
104 // quarantine and swallowing the cache.
105 if (TSDRegistryT::ThreadTSD.DestructorIterations > 1) {
106 TSDRegistryT::ThreadTSD.DestructorIterations--;
107 // If pthread_setspecific fails, we will go ahead with the teardown.
108 if (LIKELY(pthread_setspecific(Instance->getTSDRegistry()->PThreadKey,
112 TSDRegistryT::ThreadTSD.commitBack(Instance);
113 TSDRegistryT::State = ThreadState::TornDown;
118 #endif // SCUDO_TSD_EXCLUSIVE_H_