1 //===- RWMutex.h - Reader/Writer Mutual Exclusion Lock ----------*- 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 // This file declares the llvm::sys::RWMutex class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_SUPPORT_RWMUTEX_H
14 #define LLVM_SUPPORT_RWMUTEX_H
15 
16 #include "llvm/Config/llvm-config.h"
17 #include "llvm/Support/Threading.h"
18 #include <cassert>
19 #include <mutex>
20 #include <shared_mutex>
21 
22 #if defined(__APPLE__)
23 #define LLVM_USE_RW_MUTEX_IMPL
24 #endif
25 
26 namespace llvm {
27 namespace sys {
28 
29 #if defined(LLVM_USE_RW_MUTEX_IMPL)
30 /// Platform agnostic RWMutex class.
31 class RWMutexImpl {
32   /// @name Constructors
33   /// @{
34 public:
35   /// Initializes the lock but doesn't acquire it.
36   /// Default Constructor.
37   explicit RWMutexImpl();
38 
39   /// @}
40   /// @name Do Not Implement
41   /// @{
42   RWMutexImpl(const RWMutexImpl &original) = delete;
43   RWMutexImpl &operator=(const RWMutexImpl &) = delete;
44   /// @}
45 
46   /// Releases and removes the lock
47   /// Destructor
48   ~RWMutexImpl();
49 
50   /// @}
51   /// @name Methods
52   /// @{
53 public:
54   /// Attempts to unconditionally acquire the lock in reader mode. If the
55   /// lock is held by a writer, this method will wait until it can acquire
56   /// the lock.
57   /// @returns false if any kind of error occurs, true otherwise.
58   /// Unconditionally acquire the lock in reader mode.
59   bool lock_shared();
60 
61   /// Attempts to release the lock in reader mode.
62   /// @returns false if any kind of error occurs, true otherwise.
63   /// Unconditionally release the lock in reader mode.
64   bool unlock_shared();
65 
66   /// Attempts to unconditionally acquire the lock in reader mode. If the
67   /// lock is held by any readers, this method will wait until it can
68   /// acquire the lock.
69   /// @returns false if any kind of error occurs, true otherwise.
70   /// Unconditionally acquire the lock in writer mode.
71   bool lock();
72 
73   /// Attempts to release the lock in writer mode.
74   /// @returns false if any kind of error occurs, true otherwise.
75   /// Unconditionally release the lock in write mode.
76   bool unlock();
77 
78   //@}
79   /// @name Platform Dependent Data
80   /// @{
81 private:
82 #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
83   void *data_ = nullptr; ///< We don't know what the data will be
84 #endif
85 };
86 #endif
87 
88 /// SmartMutex - An R/W mutex with a compile time constant parameter that
89 /// indicates whether this mutex should become a no-op when we're not
90 /// running in multithreaded mode.
91 template <bool mt_only> class SmartRWMutex {
92 #if !defined(LLVM_USE_RW_MUTEX_IMPL)
93   std::shared_mutex impl;
94 #else
95   RWMutexImpl impl;
96 #endif
97   unsigned readers = 0;
98   unsigned writers = 0;
99 
100 public:
lock_shared()101   bool lock_shared() {
102     if (!mt_only || llvm_is_multithreaded()) {
103       impl.lock_shared();
104       return true;
105     }
106 
107     // Single-threaded debugging code.  This would be racy in multithreaded
108     // mode, but provides not basic checks in single threaded mode.
109     ++readers;
110     return true;
111   }
112 
unlock_shared()113   bool unlock_shared() {
114     if (!mt_only || llvm_is_multithreaded()) {
115       impl.unlock_shared();
116       return true;
117     }
118 
119     // Single-threaded debugging code.  This would be racy in multithreaded
120     // mode, but provides not basic checks in single threaded mode.
121     assert(readers > 0 && "Reader lock not acquired before release!");
122     --readers;
123     return true;
124   }
125 
lock()126   bool lock() {
127     if (!mt_only || llvm_is_multithreaded()) {
128       impl.lock();
129       return true;
130     }
131 
132     // Single-threaded debugging code.  This would be racy in multithreaded
133     // mode, but provides not basic checks in single threaded mode.
134     assert(writers == 0 && "Writer lock already acquired!");
135     ++writers;
136     return true;
137   }
138 
unlock()139   bool unlock() {
140     if (!mt_only || llvm_is_multithreaded()) {
141       impl.unlock();
142       return true;
143     }
144 
145     // Single-threaded debugging code.  This would be racy in multithreaded
146     // mode, but provides not basic checks in single threaded mode.
147     assert(writers == 1 && "Writer lock not acquired before release!");
148     --writers;
149     return true;
150   }
151 };
152 
153 typedef SmartRWMutex<false> RWMutex;
154 
155 /// ScopedReader - RAII acquisition of a reader lock
156 #if !defined(LLVM_USE_RW_MUTEX_IMPL)
157 template <bool mt_only>
158 using SmartScopedReader = const std::shared_lock<SmartRWMutex<mt_only>>;
159 #else
160 template <bool mt_only> struct SmartScopedReader {
161   SmartRWMutex<mt_only> &mutex;
162 
SmartScopedReaderSmartScopedReader163   explicit SmartScopedReader(SmartRWMutex<mt_only> &m) : mutex(m) {
164     mutex.lock_shared();
165   }
166 
~SmartScopedReaderSmartScopedReader167   ~SmartScopedReader() { mutex.unlock_shared(); }
168 };
169 #endif
170 typedef SmartScopedReader<false> ScopedReader;
171 
172 /// ScopedWriter - RAII acquisition of a writer lock
173 #if !defined(LLVM_USE_RW_MUTEX_IMPL)
174 template <bool mt_only>
175 using SmartScopedWriter = std::lock_guard<SmartRWMutex<mt_only>>;
176 #else
177 template <bool mt_only> struct SmartScopedWriter {
178   SmartRWMutex<mt_only> &mutex;
179 
SmartScopedWriterSmartScopedWriter180   explicit SmartScopedWriter(SmartRWMutex<mt_only> &m) : mutex(m) {
181     mutex.lock();
182   }
183 
~SmartScopedWriterSmartScopedWriter184   ~SmartScopedWriter() { mutex.unlock(); }
185 };
186 #endif
187 typedef SmartScopedWriter<false> ScopedWriter;
188 
189 } // end namespace sys
190 } // end namespace llvm
191 
192 #endif // LLVM_SUPPORT_RWMUTEX_H
193