1*76559068SAndroid Build Coastguard Worker //===-- mutex.h -------------------------------------------------*- C++ -*-===// 2*76559068SAndroid Build Coastguard Worker // 3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information. 5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*76559068SAndroid Build Coastguard Worker // 7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===// 8*76559068SAndroid Build Coastguard Worker 9*76559068SAndroid Build Coastguard Worker #ifndef SCUDO_MUTEX_H_ 10*76559068SAndroid Build Coastguard Worker #define SCUDO_MUTEX_H_ 11*76559068SAndroid Build Coastguard Worker 12*76559068SAndroid Build Coastguard Worker #include "atomic_helpers.h" 13*76559068SAndroid Build Coastguard Worker #include "common.h" 14*76559068SAndroid Build Coastguard Worker #include "thread_annotations.h" 15*76559068SAndroid Build Coastguard Worker 16*76559068SAndroid Build Coastguard Worker #include <string.h> 17*76559068SAndroid Build Coastguard Worker 18*76559068SAndroid Build Coastguard Worker #if SCUDO_FUCHSIA 19*76559068SAndroid Build Coastguard Worker #include <lib/sync/mutex.h> // for sync_mutex_t 20*76559068SAndroid Build Coastguard Worker #endif 21*76559068SAndroid Build Coastguard Worker 22*76559068SAndroid Build Coastguard Worker namespace scudo { 23*76559068SAndroid Build Coastguard Worker 24*76559068SAndroid Build Coastguard Worker class CAPABILITY("mutex") HybridMutex { 25*76559068SAndroid Build Coastguard Worker public: 26*76559068SAndroid Build Coastguard Worker bool tryLock() TRY_ACQUIRE(true); lock()27*76559068SAndroid Build Coastguard Worker NOINLINE void lock() ACQUIRE() { 28*76559068SAndroid Build Coastguard Worker if (LIKELY(tryLock())) 29*76559068SAndroid Build Coastguard Worker return; 30*76559068SAndroid Build Coastguard Worker // The compiler may try to fully unroll the loop, ending up in a 31*76559068SAndroid Build Coastguard Worker // NumberOfTries*NumberOfYields block of pauses mixed with tryLocks. This 32*76559068SAndroid Build Coastguard Worker // is large, ugly and unneeded, a compact loop is better for our purpose 33*76559068SAndroid Build Coastguard Worker // here. Use a pragma to tell the compiler not to unroll the loop. 34*76559068SAndroid Build Coastguard Worker #ifdef __clang__ 35*76559068SAndroid Build Coastguard Worker #pragma nounroll 36*76559068SAndroid Build Coastguard Worker #endif 37*76559068SAndroid Build Coastguard Worker for (u8 I = 0U; I < NumberOfTries; I++) { 38*76559068SAndroid Build Coastguard Worker delayLoop(); 39*76559068SAndroid Build Coastguard Worker if (tryLock()) 40*76559068SAndroid Build Coastguard Worker return; 41*76559068SAndroid Build Coastguard Worker } 42*76559068SAndroid Build Coastguard Worker lockSlow(); 43*76559068SAndroid Build Coastguard Worker } 44*76559068SAndroid Build Coastguard Worker void unlock() RELEASE(); 45*76559068SAndroid Build Coastguard Worker 46*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): In general, we may want to assert the owner of lock as 47*76559068SAndroid Build Coastguard Worker // well. Given the current uses of HybridMutex, it's acceptable without 48*76559068SAndroid Build Coastguard Worker // asserting the owner. Re-evaluate this when we have certain scenarios which 49*76559068SAndroid Build Coastguard Worker // requires a more fine-grained lock granularity. assertHeld()50*76559068SAndroid Build Coastguard Worker ALWAYS_INLINE void assertHeld() ASSERT_CAPABILITY(this) { 51*76559068SAndroid Build Coastguard Worker if (SCUDO_DEBUG) 52*76559068SAndroid Build Coastguard Worker assertHeldImpl(); 53*76559068SAndroid Build Coastguard Worker } 54*76559068SAndroid Build Coastguard Worker 55*76559068SAndroid Build Coastguard Worker private: delayLoop()56*76559068SAndroid Build Coastguard Worker void delayLoop() { 57*76559068SAndroid Build Coastguard Worker // The value comes from the average time spent in accessing caches (which 58*76559068SAndroid Build Coastguard Worker // are the fastest operations) so that we are unlikely to wait too long for 59*76559068SAndroid Build Coastguard Worker // fast operations. 60*76559068SAndroid Build Coastguard Worker constexpr u32 SpinTimes = 16; 61*76559068SAndroid Build Coastguard Worker volatile u32 V = 0; 62*76559068SAndroid Build Coastguard Worker for (u32 I = 0; I < SpinTimes; ++I) { 63*76559068SAndroid Build Coastguard Worker u32 Tmp = V + 1; 64*76559068SAndroid Build Coastguard Worker V = Tmp; 65*76559068SAndroid Build Coastguard Worker } 66*76559068SAndroid Build Coastguard Worker } 67*76559068SAndroid Build Coastguard Worker 68*76559068SAndroid Build Coastguard Worker void assertHeldImpl(); 69*76559068SAndroid Build Coastguard Worker 70*76559068SAndroid Build Coastguard Worker // TODO(chiahungduan): Adapt this value based on scenarios. E.g., primary and 71*76559068SAndroid Build Coastguard Worker // secondary allocator have different allocation times. 72*76559068SAndroid Build Coastguard Worker static constexpr u8 NumberOfTries = 32U; 73*76559068SAndroid Build Coastguard Worker 74*76559068SAndroid Build Coastguard Worker #if SCUDO_LINUX 75*76559068SAndroid Build Coastguard Worker atomic_u32 M = {}; 76*76559068SAndroid Build Coastguard Worker #elif SCUDO_FUCHSIA 77*76559068SAndroid Build Coastguard Worker sync_mutex_t M = {}; 78*76559068SAndroid Build Coastguard Worker #endif 79*76559068SAndroid Build Coastguard Worker 80*76559068SAndroid Build Coastguard Worker void lockSlow() ACQUIRE(); 81*76559068SAndroid Build Coastguard Worker }; 82*76559068SAndroid Build Coastguard Worker 83*76559068SAndroid Build Coastguard Worker class SCOPED_CAPABILITY ScopedLock { 84*76559068SAndroid Build Coastguard Worker public: ScopedLock(HybridMutex & M)85*76559068SAndroid Build Coastguard Worker explicit ScopedLock(HybridMutex &M) ACQUIRE(M) : Mutex(M) { Mutex.lock(); } RELEASE()86*76559068SAndroid Build Coastguard Worker ~ScopedLock() RELEASE() { Mutex.unlock(); } 87*76559068SAndroid Build Coastguard Worker 88*76559068SAndroid Build Coastguard Worker private: 89*76559068SAndroid Build Coastguard Worker HybridMutex &Mutex; 90*76559068SAndroid Build Coastguard Worker 91*76559068SAndroid Build Coastguard Worker ScopedLock(const ScopedLock &) = delete; 92*76559068SAndroid Build Coastguard Worker void operator=(const ScopedLock &) = delete; 93*76559068SAndroid Build Coastguard Worker }; 94*76559068SAndroid Build Coastguard Worker 95*76559068SAndroid Build Coastguard Worker } // namespace scudo 96*76559068SAndroid Build Coastguard Worker 97*76559068SAndroid Build Coastguard Worker #endif // SCUDO_MUTEX_H_ 98