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); lock()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 yieldProcessor(NumberOfYields); 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. assertHeld()50 ALWAYS_INLINE void assertHeld() ASSERT_CAPABILITY(this) { 51 if (SCUDO_DEBUG) 52 assertHeldImpl(); 53 } 54 55 private: 56 void assertHeldImpl(); 57 58 static constexpr u8 NumberOfTries = 8U; 59 static constexpr u8 NumberOfYields = 8U; 60 61 #if SCUDO_LINUX 62 atomic_u32 M = {}; 63 #elif SCUDO_FUCHSIA 64 sync_mutex_t M = {}; 65 #endif 66 67 void lockSlow() ACQUIRE(); 68 }; 69 70 class SCOPED_CAPABILITY ScopedLock { 71 public: ScopedLock(HybridMutex & M)72 explicit ScopedLock(HybridMutex &M) ACQUIRE(M) : Mutex(M) { Mutex.lock(); } RELEASE()73 ~ScopedLock() RELEASE() { Mutex.unlock(); } 74 75 private: 76 HybridMutex &Mutex; 77 78 ScopedLock(const ScopedLock &) = delete; 79 void operator=(const ScopedLock &) = delete; 80 }; 81 82 } // namespace scudo 83 84 #endif // SCUDO_MUTEX_H_ 85