xref: /freebsd/contrib/llvm-project/compiler-rt/lib/scudo/standalone/tsd.h (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
10b57cec5SDimitry Andric //===-- tsd.h ---------------------------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #ifndef SCUDO_TSD_H_
100b57cec5SDimitry Andric #define SCUDO_TSD_H_
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #include "atomic_helpers.h"
130b57cec5SDimitry Andric #include "common.h"
140b57cec5SDimitry Andric #include "mutex.h"
15*06c3fb27SDimitry Andric #include "thread_annotations.h"
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include <limits.h> // for PTHREAD_DESTRUCTOR_ITERATIONS
18480093f4SDimitry Andric #include <pthread.h>
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric // With some build setups, this might still not be defined.
210b57cec5SDimitry Andric #ifndef PTHREAD_DESTRUCTOR_ITERATIONS
220b57cec5SDimitry Andric #define PTHREAD_DESTRUCTOR_ITERATIONS 4
230b57cec5SDimitry Andric #endif
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric namespace scudo {
260b57cec5SDimitry Andric 
275ffd83dbSDimitry Andric template <class Allocator> struct alignas(SCUDO_CACHE_LINE_SIZE) TSD {
28fe6060f1SDimitry Andric   using ThisT = TSD<Allocator>;
29fe6060f1SDimitry Andric   u8 DestructorIterations = 0;
300b57cec5SDimitry Andric 
31*06c3fb27SDimitry Andric   void init(Allocator *Instance) NO_THREAD_SAFETY_ANALYSIS {
32fe6060f1SDimitry Andric     DCHECK_EQ(DestructorIterations, 0U);
33fe6060f1SDimitry Andric     DCHECK(isAligned(reinterpret_cast<uptr>(this), alignof(ThisT)));
340b57cec5SDimitry Andric     Instance->initCache(&Cache);
350b57cec5SDimitry Andric     DestructorIterations = PTHREAD_DESTRUCTOR_ITERATIONS;
360b57cec5SDimitry Andric   }
370b57cec5SDimitry Andric 
38*06c3fb27SDimitry Andric   inline bool tryLock() NO_THREAD_SAFETY_ANALYSIS {
390b57cec5SDimitry Andric     if (Mutex.tryLock()) {
400b57cec5SDimitry Andric       atomic_store_relaxed(&Precedence, 0);
410b57cec5SDimitry Andric       return true;
420b57cec5SDimitry Andric     }
430b57cec5SDimitry Andric     if (atomic_load_relaxed(&Precedence) == 0)
44*06c3fb27SDimitry Andric       atomic_store_relaxed(&Precedence,
45*06c3fb27SDimitry Andric                            static_cast<uptr>(getMonotonicTimeFast() >>
46*06c3fb27SDimitry Andric                                              FIRST_32_SECOND_64(16, 0)));
470b57cec5SDimitry Andric     return false;
480b57cec5SDimitry Andric   }
49*06c3fb27SDimitry Andric   inline void lock() NO_THREAD_SAFETY_ANALYSIS {
500b57cec5SDimitry Andric     atomic_store_relaxed(&Precedence, 0);
510b57cec5SDimitry Andric     Mutex.lock();
520b57cec5SDimitry Andric   }
53*06c3fb27SDimitry Andric   inline void unlock() NO_THREAD_SAFETY_ANALYSIS { Mutex.unlock(); }
54480093f4SDimitry Andric   inline uptr getPrecedence() { return atomic_load_relaxed(&Precedence); }
550b57cec5SDimitry Andric 
56*06c3fb27SDimitry Andric   void commitBack(Allocator *Instance) ASSERT_CAPABILITY(Mutex) {
57*06c3fb27SDimitry Andric     Instance->commitBack(this);
58*06c3fb27SDimitry Andric   }
59*06c3fb27SDimitry Andric 
60*06c3fb27SDimitry Andric   // Ideally, we may want to assert that all the operations on
61*06c3fb27SDimitry Andric   // Cache/QuarantineCache always have the `Mutex` acquired. However, the
62*06c3fb27SDimitry Andric   // current architecture of accessing TSD is not easy to cooperate with the
63*06c3fb27SDimitry Andric   // thread-safety analysis because of pointer aliasing. So now we just add the
64*06c3fb27SDimitry Andric   // assertion on the getters of Cache/QuarantineCache.
65*06c3fb27SDimitry Andric   //
66*06c3fb27SDimitry Andric   // TODO(chiahungduan): Ideally, we want to do `Mutex.assertHeld` but acquiring
67*06c3fb27SDimitry Andric   // TSD doesn't always require holding the lock. Add this assertion while the
68*06c3fb27SDimitry Andric   // lock is always acquired.
69*06c3fb27SDimitry Andric   typename Allocator::CacheT &getCache() ASSERT_CAPABILITY(Mutex) {
70*06c3fb27SDimitry Andric     return Cache;
71*06c3fb27SDimitry Andric   }
72*06c3fb27SDimitry Andric   typename Allocator::QuarantineCacheT &getQuarantineCache()
73*06c3fb27SDimitry Andric       ASSERT_CAPABILITY(Mutex) {
74*06c3fb27SDimitry Andric     return QuarantineCache;
75*06c3fb27SDimitry Andric   }
76*06c3fb27SDimitry Andric 
770b57cec5SDimitry Andric private:
780b57cec5SDimitry Andric   HybridMutex Mutex;
79fe6060f1SDimitry Andric   atomic_uptr Precedence = {};
80*06c3fb27SDimitry Andric 
81*06c3fb27SDimitry Andric   typename Allocator::CacheT Cache GUARDED_BY(Mutex);
82*06c3fb27SDimitry Andric   typename Allocator::QuarantineCacheT QuarantineCache GUARDED_BY(Mutex);
830b57cec5SDimitry Andric };
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric } // namespace scudo
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric #endif // SCUDO_TSD_H_
88