10b57cec5SDimitry Andric //===-- mutex.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_MUTEX_H_ 100b57cec5SDimitry Andric #define SCUDO_MUTEX_H_ 110b57cec5SDimitry Andric 120b57cec5SDimitry Andric #include "atomic_helpers.h" 130b57cec5SDimitry Andric #include "common.h" 1406c3fb27SDimitry Andric #include "thread_annotations.h" 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include <string.h> 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #if SCUDO_FUCHSIA 190b57cec5SDimitry Andric #include <lib/sync/mutex.h> // for sync_mutex_t 200b57cec5SDimitry Andric #endif 210b57cec5SDimitry Andric 220b57cec5SDimitry Andric namespace scudo { 230b57cec5SDimitry Andric 2406c3fb27SDimitry Andric class CAPABILITY("mutex") HybridMutex { 250b57cec5SDimitry Andric public: 2606c3fb27SDimitry Andric bool tryLock() TRY_ACQUIRE(true); 2706c3fb27SDimitry Andric NOINLINE void lock() ACQUIRE() { 2868d75effSDimitry Andric if (LIKELY(tryLock())) 290b57cec5SDimitry Andric return; 300b57cec5SDimitry Andric // The compiler may try to fully unroll the loop, ending up in a 310b57cec5SDimitry Andric // NumberOfTries*NumberOfYields block of pauses mixed with tryLocks. This 320b57cec5SDimitry Andric // is large, ugly and unneeded, a compact loop is better for our purpose 330b57cec5SDimitry Andric // here. Use a pragma to tell the compiler not to unroll the loop. 340b57cec5SDimitry Andric #ifdef __clang__ 350b57cec5SDimitry Andric #pragma nounroll 360b57cec5SDimitry Andric #endif 370b57cec5SDimitry Andric for (u8 I = 0U; I < NumberOfTries; I++) { 38*5f757f3fSDimitry Andric delayLoop(); 390b57cec5SDimitry Andric if (tryLock()) 400b57cec5SDimitry Andric return; 410b57cec5SDimitry Andric } 420b57cec5SDimitry Andric lockSlow(); 430b57cec5SDimitry Andric } 4406c3fb27SDimitry Andric void unlock() RELEASE(); 4506c3fb27SDimitry Andric 4606c3fb27SDimitry Andric // TODO(chiahungduan): In general, we may want to assert the owner of lock as 4706c3fb27SDimitry Andric // well. Given the current uses of HybridMutex, it's acceptable without 4806c3fb27SDimitry Andric // asserting the owner. Re-evaluate this when we have certain scenarios which 4906c3fb27SDimitry Andric // requires a more fine-grained lock granularity. 5006c3fb27SDimitry Andric ALWAYS_INLINE void assertHeld() ASSERT_CAPABILITY(this) { 5106c3fb27SDimitry Andric if (SCUDO_DEBUG) 5206c3fb27SDimitry Andric assertHeldImpl(); 5306c3fb27SDimitry Andric } 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric private: 56*5f757f3fSDimitry Andric void delayLoop() { 57*5f757f3fSDimitry Andric // The value comes from the average time spent in accessing caches (which 58*5f757f3fSDimitry Andric // are the fastest operations) so that we are unlikely to wait too long for 59*5f757f3fSDimitry Andric // fast operations. 60*5f757f3fSDimitry Andric constexpr u32 SpinTimes = 16; 61*5f757f3fSDimitry Andric volatile u32 V = 0; 62*5f757f3fSDimitry Andric for (u32 I = 0; I < SpinTimes; ++I) { 63*5f757f3fSDimitry Andric u32 Tmp = V + 1; 64*5f757f3fSDimitry Andric V = Tmp; 65*5f757f3fSDimitry Andric } 66*5f757f3fSDimitry Andric } 67*5f757f3fSDimitry Andric 6806c3fb27SDimitry Andric void assertHeldImpl(); 6906c3fb27SDimitry Andric 70*5f757f3fSDimitry Andric // TODO(chiahungduan): Adapt this value based on scenarios. E.g., primary and 71*5f757f3fSDimitry Andric // secondary allocator have different allocation times. 72*5f757f3fSDimitry Andric static constexpr u8 NumberOfTries = 32U; 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric #if SCUDO_LINUX 75fe6060f1SDimitry Andric atomic_u32 M = {}; 760b57cec5SDimitry Andric #elif SCUDO_FUCHSIA 77fe6060f1SDimitry Andric sync_mutex_t M = {}; 780b57cec5SDimitry Andric #endif 790b57cec5SDimitry Andric 8006c3fb27SDimitry Andric void lockSlow() ACQUIRE(); 810b57cec5SDimitry Andric }; 820b57cec5SDimitry Andric 8306c3fb27SDimitry Andric class SCOPED_CAPABILITY ScopedLock { 840b57cec5SDimitry Andric public: 8506c3fb27SDimitry Andric explicit ScopedLock(HybridMutex &M) ACQUIRE(M) : Mutex(M) { Mutex.lock(); } 8606c3fb27SDimitry Andric ~ScopedLock() RELEASE() { Mutex.unlock(); } 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric private: 890b57cec5SDimitry Andric HybridMutex &Mutex; 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric ScopedLock(const ScopedLock &) = delete; 920b57cec5SDimitry Andric void operator=(const ScopedLock &) = delete; 930b57cec5SDimitry Andric }; 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric } // namespace scudo 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric #endif // SCUDO_MUTEX_H_ 98