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