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