mCRL2
Loading...
Searching...
No Matches
shared_mutex.h
Go to the documentation of this file.
1// Author(s): Maurice Laveaux
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8//
9
10#ifndef MCRL2_UTILITIES_DETAIL_SHARED_MUTEX_H
11#define MCRL2_UTILITIES_DETAIL_SHARED_MUTEX_H
12
13#include <assert.h>
14#include <algorithm>
15#include <atomic>
16#include <memory>
17#include <mutex>
18#include <vector>
19
22
23
24namespace mcrl2::utilities
25{
26
27// Forward declaration.
28class shared_mutex;
29
32{
33public:
34
36 inline
37 void lock_shared();
38
40 inline
41 void unlock_shared();
42
44 {
45 if (is_locked)
46 {
48 }
49 }
50
51private:
52 friend class shared_mutex;
53
55 : m_mutex(mutex)
56 {}
57
59 bool is_locked = true;
60};
61
64{
65public:
67 void unlock();
68
70 {
71 if (is_locked)
72 {
73 unlock();
74 }
75 }
76
77private:
78 friend class shared_mutex;
79
81 : m_mutex(mutex)
82 {}
83
85 bool is_locked = true;
86};
87
89{
91 std::vector<shared_mutex*> other;
92
94 std::mutex mutex;
95
97 inline
99 {
100 std::lock_guard guard(mutex);
101 other.emplace_back(shared_mutex);
102 }
103
104 // Removes a shared mutex from the data
106 {
107 std::lock_guard guard(mutex);
108 auto it = std::find(other.begin(), other.end(), shared_mutex);
109 assert(it != other.end());
110
111 other.erase(it);
112 }
113};
114
118{
119public:
121 : m_shared(std::make_shared<shared_mutex_data>())
122 {
123 m_shared->register_mutex(this);
124 }
125
127 {
128 m_shared->unregister_mutex(this);
129 }
130
133 : m_shared(other.m_shared)
134 {
135 m_shared->register_mutex(this);
136 }
137
139 : m_shared(other.m_shared)
140 {
141 m_shared->register_mutex(this);
142 m_shared->unregister_mutex(&other);
143 }
144
146 {
147 if (this != &other)
148 {
149 // Remove ourselves, and register into the other shared.
150 m_shared->unregister_mutex(this);
151
152 m_shared = other.m_shared;
153 m_shared->register_mutex(this);
154 }
155 return *this;
156 }
157
159 {
160 if (this != &other)
161 {
162 m_shared->unregister_mutex(this);
163
164 m_shared = other.m_shared;
165 m_shared->register_mutex(this);
166 m_shared->unregister_mutex(&other);
167 }
168 return *this;
169 }
170
171 // Obtain exclusive access to the busy-forbidden lock.
172 inline
174 {
176 {
177 // Shared and exclusive sections MUST be disjoint.
178 assert(!m_busy_flag);
179
180 // Only one thread can halt everything.
181 m_shared->mutex.lock();
182
183 assert(std::find(m_shared->other.begin(), m_shared->other.end(), this) != m_shared->other.end());
184
185 // Indicate that threads must wait.
186 for (auto& mutex : m_shared->other)
187 {
188 if (mutex != this)
189 {
190 mutex->set_forbidden(true);
191 }
192 }
193
194 // Wait for all pools to indicate that they are not busy.
195 for (const auto& mutex : m_shared->other)
196 {
197 if (mutex != this)
198 {
199 mutex->wait_for_busy();
200 }
201 }
202 }
203
204 return lock_guard(*this);
205 }
206
207 // Release exclusive access to the busy-forbidden lock.
208 inline
209 void unlock()
210 {
211 unlock_impl();
212 }
213
216 inline
218 {
220 return shared_guard(*this);
221 }
222
224 bool is_shared_locked() const
225 {
226 return m_lock_depth != 0;
227 }
228
229private:
230 friend class lock_guard;
231 friend class shared_guard;
232 friend class shared_mutex_pool;
233
235 {
237 {
238 for (auto& mutex : m_shared->other)
239 {
240 mutex->set_forbidden(false);
241 }
242
243 m_shared->mutex.unlock();
244 }
245 }
246
247 inline
249 {
251 {
252 assert(!m_busy_flag);
253 m_busy_flag.store(true);
254
255 // Wait for the forbidden flag to become false.
256 while (m_forbidden_flag.load())
257 {
258 m_busy_flag = false;
259
260 // Wait for the global lock.
261 m_shared->mutex.lock();
262 m_shared->mutex.unlock();
263
264 m_busy_flag = true;
265 }
266 }
267
268 ++m_lock_depth;
269 }
270
271 // Release shared access to the busy-forbidden lock.
272 inline
274 {
276
277 --m_lock_depth;
279 {
280 assert(m_busy_flag);
281 m_busy_flag.store(false, std::memory_order_release);
282 }
283 }
284
286 inline
287 bool is_busy() const
288 {
289 return m_busy_flag.load();
290 }
291
293 inline
294 void wait_for_busy() const
295 {
296 while (m_busy_flag.load()) { /* wait */ };
297 }
298
299 inline
300 void set_forbidden(bool value)
301 {
302 m_forbidden_flag.store(value);
303 }
304
306 std::atomic<bool> m_busy_flag = false;
307 std::atomic<bool> m_forbidden_flag = false;
308
311 std::size_t m_lock_depth = 0;
312
313 std::shared_ptr<shared_mutex_data> m_shared;
314};
315
316inline
318{
319 // Uses the internal implementation since we don't need a shared_guard.
321 is_locked = true;
322}
323
324inline
326{
328 is_locked = false;
329}
330
331inline
333{
334 m_mutex.unlock();
335 is_locked = false;
336}
337
338} // namespace mcrl2::utilities
339
340#endif // MCRL2_UTILITIES_DETAIL_SHARED_MUTEX_H
An exclusive lock guard for the shared_mutex.
void unlock()
Unlocks the acquired shared guard explicitly. Otherwise, performed in destructor.
lock_guard(shared_mutex &mutex)
This is simply an exclusive lock based on the standard library with the ability to perform no locks w...
Definition mutex.h:23
Inherit from this class to prevent it from being copyable.
Definition noncopyable.h:21
A shared lock guard for the shared_mutex.
void lock_shared()
Locks the guard again explicitly.
shared_guard(shared_mutex &mutex)
void unlock_shared()
Unlocks the acquired shared guard explicitly. Otherwise, performed in destructor.
std::atomic< bool > m_forbidden_flag
std::atomic< bool > m_busy_flag
A boolean flag indicating whether this thread is working inside the global aterm pool.
shared_mutex(shared_mutex &&other)
void wait_for_busy() const
Waits for the busy flag to become false.
shared_mutex(const shared_mutex &other)
The copy/move constructor/assignment should not be called while any lock_guard or shared_guard is ali...
std::size_t m_lock_depth
It can happen that un/lock_shared calls are nested, so keep track of the nesting depth and only actua...
std::shared_ptr< shared_mutex_data > m_shared
shared_mutex & operator=(const shared_mutex &other)
shared_mutex & operator=(shared_mutex &&other)
static constexpr bool GlobalThreadSafe
Enables thread safety for the whole toolset.
STL namespace.
std::mutex mutex
Mutex for adding/removing shared_guards.
void unregister_mutex(shared_mutex *shared_mutex)
void register_mutex(shared_mutex *shared_mutex)
Adds a shared mutex to the data.
std::vector< shared_mutex * > other
The list of other mutexes.