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);
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.
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:
72   explicit ScopedLock(HybridMutex &M) ACQUIRE(M) : Mutex(M) { Mutex.lock(); }
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