• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef TOOLS_SRC_CMD_REMOTE_COMPILE_RWMUTEX_H_
16 #define TOOLS_SRC_CMD_REMOTE_COMPILE_RWMUTEX_H_
17 
18 #include <condition_variable>  // NOLINT
19 #include <mutex>               // NOLINT
20 
21 ////////////////////////////////////////////////////////////////////////////////
22 // RWMutex
23 ////////////////////////////////////////////////////////////////////////////////
24 
25 /// A RWMutex is a reader/writer mutual exclusion lock.
26 /// The lock can be held by an arbitrary number of readers or a single writer.
27 /// Also known as a shared mutex.
28 class RWMutex {
29  public:
30   inline RWMutex() = default;
31 
32   /// lockReader() locks the mutex for reading.
33   /// Multiple read locks can be held while there are no writer locks.
34   inline void lockReader();
35 
36   /// unlockReader() unlocks the mutex for reading.
37   inline void unlockReader();
38 
39   /// lockWriter() locks the mutex for writing.
40   /// If the lock is already locked for reading or writing, lockWriter blocks
41   /// until the lock is available.
42   inline void lockWriter();
43 
44   /// unlockWriter() unlocks the mutex for writing.
45   inline void unlockWriter();
46 
47  private:
48   RWMutex(const RWMutex&) = delete;
49   RWMutex& operator=(const RWMutex&) = delete;
50 
51   int readLocks = 0;
52   int pendingWriteLocks = 0;
53   std::mutex mutex;
54   std::condition_variable cv;
55 };
56 
lockReader()57 void RWMutex::lockReader() {
58   std::unique_lock<std::mutex> lock(mutex);
59   readLocks++;
60 }
61 
unlockReader()62 void RWMutex::unlockReader() {
63   std::unique_lock<std::mutex> lock(mutex);
64   readLocks--;
65   if (readLocks == 0 && pendingWriteLocks > 0) {
66     cv.notify_one();
67   }
68 }
69 
lockWriter()70 void RWMutex::lockWriter() {
71   std::unique_lock<std::mutex> lock(mutex);
72   if (readLocks > 0) {
73     pendingWriteLocks++;
74     cv.wait(lock, [&] { return readLocks == 0; });
75     pendingWriteLocks--;
76   }
77   lock.release();  // Keep lock held
78 }
79 
unlockWriter()80 void RWMutex::unlockWriter() {
81   if (pendingWriteLocks > 0) {
82     cv.notify_one();
83   }
84   mutex.unlock();
85 }
86 
87 ////////////////////////////////////////////////////////////////////////////////
88 // RLock
89 ////////////////////////////////////////////////////////////////////////////////
90 
91 /// RLock is a RAII read lock helper for a RWMutex.
92 class RLock {
93  public:
94   /// Constructor.
95   /// Locks `mutex` with a read-lock for the lifetime of the WLock.
96   /// @param mutex the mutex
97   explicit inline RLock(RWMutex& mutex);
98   /// Destructor.
99   /// Unlocks the RWMutex.
100   inline ~RLock();
101 
102   /// Move constructor
103   /// @param other the other RLock to move into this RLock.
104   inline RLock(RLock&& other);
105   /// Move assignment operator
106   /// @param other the other RLock to move into this RLock.
107   /// @returns this RLock so calls can be chained
108   inline RLock& operator=(RLock&& other);
109 
110  private:
111   RLock(const RLock&) = delete;
112   RLock& operator=(const RLock&) = delete;
113 
114   RWMutex* m;
115 };
116 
RLock(RWMutex & mutex)117 RLock::RLock(RWMutex& mutex) : m(&mutex) {
118   m->lockReader();
119 }
120 
~RLock()121 RLock::~RLock() {
122   if (m != nullptr) {
123     m->unlockReader();
124   }
125 }
126 
RLock(RLock && other)127 RLock::RLock(RLock&& other) {
128   m = other.m;
129   other.m = nullptr;
130 }
131 
132 RLock& RLock::operator=(RLock&& other) {
133   m = other.m;
134   other.m = nullptr;
135   return *this;
136 }
137 
138 ////////////////////////////////////////////////////////////////////////////////
139 // WLock
140 ////////////////////////////////////////////////////////////////////////////////
141 
142 /// WLock is a RAII write lock helper for a RWMutex.
143 class WLock {
144  public:
145   /// Constructor.
146   /// Locks `mutex` with a write-lock for the lifetime of the WLock.
147   /// @param mutex the mutex
148   explicit inline WLock(RWMutex& mutex);
149 
150   /// Destructor.
151   /// Unlocks the RWMutex.
152   inline ~WLock();
153 
154   /// Move constructor
155   /// @param other the other WLock to move into this WLock.
156   inline WLock(WLock&& other);
157   /// Move assignment operator
158   /// @param other the other WLock to move into this WLock.
159   /// @returns this WLock so calls can be chained
160   inline WLock& operator=(WLock&& other);
161 
162  private:
163   WLock(const WLock&) = delete;
164   WLock& operator=(const WLock&) = delete;
165 
166   RWMutex* m;
167 };
168 
WLock(RWMutex & mutex)169 WLock::WLock(RWMutex& mutex) : m(&mutex) {
170   m->lockWriter();
171 }
172 
~WLock()173 WLock::~WLock() {
174   if (m != nullptr) {
175     m->unlockWriter();
176   }
177 }
178 
WLock(WLock && other)179 WLock::WLock(WLock&& other) {
180   m = other.m;
181   other.m = nullptr;
182 }
183 
184 WLock& WLock::operator=(WLock&& other) {
185   m = other.m;
186   other.m = nullptr;
187   return *this;
188 }
189 
190 #endif  // TOOLS_SRC_CMD_REMOTE_COMPILE_RWMUTEX_H_
191