//===-- mutex.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_MUTEX_H_ #define SCUDO_MUTEX_H_ #include "atomic_helpers.h" #include "common.h" #include "thread_annotations.h" #include #if SCUDO_FUCHSIA #include // for sync_mutex_t #endif namespace scudo { class CAPABILITY("mutex") HybridMutex { public: bool tryLock() TRY_ACQUIRE(true); NOINLINE void lock() ACQUIRE() { if (LIKELY(tryLock())) return; // The compiler may try to fully unroll the loop, ending up in a // NumberOfTries*NumberOfYields block of pauses mixed with tryLocks. This // is large, ugly and unneeded, a compact loop is better for our purpose // here. Use a pragma to tell the compiler not to unroll the loop. #ifdef __clang__ #pragma nounroll #endif for (u8 I = 0U; I < NumberOfTries; I++) { delayLoop(); if (tryLock()) return; } lockSlow(); } void unlock() RELEASE(); // TODO(chiahungduan): In general, we may want to assert the owner of lock as // well. Given the current uses of HybridMutex, it's acceptable without // asserting the owner. Re-evaluate this when we have certain scenarios which // requires a more fine-grained lock granularity. ALWAYS_INLINE void assertHeld() ASSERT_CAPABILITY(this) { if (SCUDO_DEBUG) assertHeldImpl(); } private: void delayLoop() { // The value comes from the average time spent in accessing caches (which // are the fastest operations) so that we are unlikely to wait too long for // fast operations. constexpr u32 SpinTimes = 16; volatile u32 V = 0; for (u32 I = 0; I < SpinTimes; ++I) { u32 Tmp = V + 1; V = Tmp; } } void assertHeldImpl(); // TODO(chiahungduan): Adapt this value based on scenarios. E.g., primary and // secondary allocator have different allocation times. static constexpr u8 NumberOfTries = 32U; #if SCUDO_LINUX atomic_u32 M = {}; #elif SCUDO_FUCHSIA sync_mutex_t M = {}; #endif void lockSlow() ACQUIRE(); }; class SCOPED_CAPABILITY ScopedLock { public: explicit ScopedLock(HybridMutex &M) ACQUIRE(M) : Mutex(M) { Mutex.lock(); } ~ScopedLock() RELEASE() { Mutex.unlock(); } private: HybridMutex &Mutex; ScopedLock(const ScopedLock &) = delete; void operator=(const ScopedLock &) = delete; }; } // namespace scudo #endif // SCUDO_MUTEX_H_