xref: /freebsd/contrib/llvm-project/compiler-rt/lib/scudo/standalone/mutex.h (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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