//===-- tsd.h ---------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef SCUDO_TSD_H_ #define SCUDO_TSD_H_ #include "atomic_helpers.h" #include "common.h" #include "mutex.h" #include "thread_annotations.h" #include // for PTHREAD_DESTRUCTOR_ITERATIONS #include // With some build setups, this might still not be defined. #ifndef PTHREAD_DESTRUCTOR_ITERATIONS #define PTHREAD_DESTRUCTOR_ITERATIONS 4 #endif namespace scudo { template struct alignas(SCUDO_CACHE_LINE_SIZE) TSD { using ThisT = TSD; u8 DestructorIterations = 0; void init(Allocator *Instance) NO_THREAD_SAFETY_ANALYSIS { DCHECK_EQ(DestructorIterations, 0U); DCHECK(isAligned(reinterpret_cast(this), alignof(ThisT))); Instance->initCache(&Cache); DestructorIterations = PTHREAD_DESTRUCTOR_ITERATIONS; } inline bool tryLock() NO_THREAD_SAFETY_ANALYSIS { if (Mutex.tryLock()) { atomic_store_relaxed(&Precedence, 0); return true; } if (atomic_load_relaxed(&Precedence) == 0) atomic_store_relaxed(&Precedence, static_cast(getMonotonicTimeFast() >> FIRST_32_SECOND_64(16, 0))); return false; } inline void lock() NO_THREAD_SAFETY_ANALYSIS { atomic_store_relaxed(&Precedence, 0); Mutex.lock(); } inline void unlock() NO_THREAD_SAFETY_ANALYSIS { Mutex.unlock(); } inline uptr getPrecedence() { return atomic_load_relaxed(&Precedence); } void commitBack(Allocator *Instance) { Instance->commitBack(this); } // As the comments attached to `getCache()`, the TSD doesn't always need to be // locked. In that case, we would only skip the check before we have all TSDs // locked in all paths. void assertLocked(bool BypassCheck) ASSERT_CAPABILITY(Mutex) { if (SCUDO_DEBUG && !BypassCheck) Mutex.assertHeld(); } // Ideally, we may want to assert that all the operations on // Cache/QuarantineCache always have the `Mutex` acquired. However, the // current architecture of accessing TSD is not easy to cooperate with the // thread-safety analysis because of pointer aliasing. So now we just add the // assertion on the getters of Cache/QuarantineCache. // // TODO(chiahungduan): Ideally, we want to do `Mutex.assertHeld` but acquiring // TSD doesn't always require holding the lock. Add this assertion while the // lock is always acquired. typename Allocator::CacheT &getCache() REQUIRES(Mutex) { return Cache; } typename Allocator::QuarantineCacheT &getQuarantineCache() REQUIRES(Mutex) { return QuarantineCache; } private: HybridMutex Mutex; atomic_uptr Precedence = {}; typename Allocator::CacheT Cache GUARDED_BY(Mutex); typename Allocator::QuarantineCacheT QuarantineCache GUARDED_BY(Mutex); }; } // namespace scudo #endif // SCUDO_TSD_H_