mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
reader_writer_lock.hpp
Go to the documentation of this file.
1#pragma once
2
3
4#include <condition_variable>
6#include <mutex>
7#include <optional>
8#include <utility>
9
10
11namespace m {
12
13namespace detail {
14
19{
21 uint16_t readers_waiting_{0};
23 uint16_t writers_waiting_{0};
26 uint16_t readers_active_{0};
28 bool writer_active_{false};
29
32
33 public:
34 uint16_t readers_waiting() const { return readers_waiting_; }
35 uint16_t readers_active() const { return readers_active_; }
36 uint16_t writers_waiting() const { return writers_waiting_; }
37 bool writer_active() const { return writer_active_; }
39
44
52 if (reader_wants_upgrade()) return false;
55 return true;
56 }
57
59 bool can_acquire_read_lock() const {
61 }
63 bool can_acquire_write_lock() const { return not writer_active() and readers_active() == 0; }
65 bool can_upgrade_lock() const {
66 M_insist(reader_wants_upgrade(), "cannot upgrade without previously requesting the upgrade");
67 M_insist(writers_waiting() > 0, "at least we must be waiting for the write lock");
68 return not writer_active() and readers_active() == 1; // are we the only active reader?
69 }
70
74 M_insist(readers_waiting() > 0, "one or more readers did not properly request a read lock in advance");
77 }
78 /* Acquires a previously requested write lock. */
81 M_insist(writers_waiting() > 0, "one or more writers did not properly request the write lock in advance");
83 writer_active_ = true;
84 }
85
91 }
92
93 /* Releases a previously acquired write lock. */
97 writer_active_ = false;
98 }
99
103 reader_wants_upgrade_ = false;
106 M_insist(readers_active() == 0, "we must have been the last active reader");
107 }
108};
109
110}
111
118{
125 std::mutex mutex_;
126
129
136 std::condition_variable cv_readers_, cv_writers_;
137
138 public:
139 void notify_readers() { cv_readers_.notify_all(); }
140 void notify_writer() { cv_writers_.notify_one(); }
141
143 void lock_read() {
144 std::unique_lock lock{mutex_};
146 cv_readers_.wait(lock, [this]() -> bool { return rw_.can_acquire_read_lock(); });
147 M_insist(lock.owns_lock());
149 }
150
152 void lock_write() {
153 std::unique_lock lock{mutex_};
155 cv_writers_.wait(lock, [this]() -> bool { return rw_.can_acquire_write_lock(); });
156 M_insist(lock.owns_lock());
158 }
159
161 [[nodiscard]] bool try_lock_read() {
162 std::unique_lock lock{mutex_};
163 if (not rw_.can_acquire_read_lock())
164 return false;
167 return true;
168 }
169
171 [[nodiscard]] bool try_lock_write() {
172 std::unique_lock lock{mutex_};
173 if (not rw_.can_acquire_write_lock())
174 return false;
177 return true;
178 }
179
193 [[nodiscard]] bool upgrade() {
194 std::unique_lock lock{mutex_};
195 if (not rw_.try_request_upgrade())
196 return false;
197
199 cv_writers_.wait(lock, [this]() -> bool { return rw_.can_upgrade_lock(); });
200 M_insist(lock.owns_lock());
202 return true;
203 }
204
205 void unlock_read() {
206 std::unique_lock lock{mutex_};
209 "if a reader wants to upgrade, it must be waiting to claim the write lock");
211 /* There is a single active reader remaining, that wants to upgrade to a writer. Notify it. */
212 lock.unlock();
214 } else if (rw_.readers_active() == 0 and rw_.writers_waiting() != 0) {
215 /* We were the last reader and there are writers waiting to acquire the lock. Notify one of them. */
216 lock.unlock();
218 }
219 }
220
222 std::unique_lock lock{mutex_};
223 M_insist(rw_.readers_active() == 0, "must not have active readers while holding the write lock");
225 if (rw_.writers_waiting() != 0) {
226 /* There are other writers waiting to acquire the lock. Notify one of them. */
227 lock.unlock();
229 } else if (rw_.writers_waiting() == 0 and rw_.readers_waiting() != 0) {
230 // We were the last writer and there are other readers waiting to acquire the lock. Notify all of them.
231 lock.unlock();
233 }
234 }
235};
236
242{
243 private:
244 std::reference_wrapper<reader_writer_mutex> rw_mutex_;
245 enum {
249 } state_{LOCK_NONE};
250
251 public:
252 explicit reader_writer_lock(reader_writer_mutex &rw_mutex) : rw_mutex_(rw_mutex) { }
255 : rw_mutex_(other.rw_mutex_)
256 , state_(std::exchange(other.state_, LOCK_NONE))
257 { }
258
260 if (state_ == LOCK_NONE) return;
261 unlock();
262 }
263
265
266 bool owns_read_lock() const { return state_ == LOCK_READ; }
267 bool owns_write_lock() const { return state_ == LOCK_WRITE; }
268
270 void lock_read() {
271 rw_mutex_.get().lock_read();
272 state_ = LOCK_READ;
273 }
274
276 void lock_write() {
277 rw_mutex_.get().lock_write();
278 state_ = LOCK_WRITE;
279 }
280
290 [[nodiscard]] bool upgrade() {
291 M_insist(state_ == LOCK_READ);
292 if (rw_mutex_.get().upgrade()) {
293 state_ = LOCK_WRITE;
294 return true;
295 } else {
296 unlock(); // prevent deadlock by giving up read lock after failing to upgrade
297 return false;
298 }
299 }
300
301 void unlock() {
302 M_insist(state_ != LOCK_NONE);
303 switch (state_) {
304 case LOCK_NONE: M_unreachable("");
305 case LOCK_READ: rw_mutex_.get().unlock_read(); break;
306 case LOCK_WRITE: rw_mutex_.get().unlock_write(); break;
307 }
308 state_ = LOCK_NONE;
309 }
310};
311
313{
314 friend void swap(read_lock &first, read_lock &second) {
315 using std::swap;
316 swap(first.rw_mutex_, second.rw_mutex_);
317 swap(first.owns_lock_, second.owns_lock_);
318 }
319
320 protected:
321 std::reference_wrapper<reader_writer_mutex> rw_mutex_;
322 bool owns_lock_{false};
323
324 public:
325 explicit read_lock(reader_writer_mutex &rw_mutex) : rw_mutex_(rw_mutex) { lock(); }
326 read_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t) : rw_mutex_(rw_mutex) { }
327 read_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t) : rw_mutex_(rw_mutex), owns_lock_{true} { }
328 read_lock(const read_lock&) = delete;
329 read_lock(read_lock &&other) : rw_mutex_(other.rw_mutex_) { swap(*this, other); }
330
332
333 read_lock & operator=(read_lock &&other) { swap(*this, other); return *this; }
334
335 bool owns_lock() const { return owns_lock_; }
336
337 void lock() { M_insist(not owns_lock()); rw_mutex_.get().lock_read(); owns_lock_ = true; }
338 void unlock() { M_insist(owns_lock()); rw_mutex_.get().unlock_read(); owns_lock_ = false; }
339};
340
342{
343 friend void swap(write_lock &first, write_lock &second) {
344 using std::swap;
345 swap(first.rw_mutex_, second.rw_mutex_);
346 swap(first.owns_lock_, second.owns_lock_);
347 }
348
349 private:
350 std::reference_wrapper<reader_writer_mutex> rw_mutex_;
351 bool owns_lock_{false};
352
353 public:
354 explicit write_lock(reader_writer_mutex &rw_mutex) : rw_mutex_(rw_mutex) { lock(); }
355 write_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t) : rw_mutex_(rw_mutex) { }
356 write_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t) : rw_mutex_(rw_mutex), owns_lock_{true} { }
357 write_lock(const write_lock&) = delete;
358 write_lock(write_lock &&other) : rw_mutex_(other.rw_mutex_) { swap(*this, other); }
359
361
362 write_lock & operator=(write_lock &&other) { swap(*this, other); return *this; }
363
364 bool owns_lock() const { return owns_lock_; }
365
366 void lock() { M_insist(not owns_lock()); rw_mutex_.get().lock_write(); owns_lock_ = true; }
367 void unlock() { M_insist(owns_lock()); rw_mutex_.get().unlock_write(); owns_lock_ = false; }
368};
369
370struct upgrade_lock : protected read_lock
371{
372 explicit upgrade_lock(reader_writer_mutex &rw_mutex) : read_lock(rw_mutex) { M_insist(owns_read_lock()); }
373 upgrade_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t) : read_lock(rw_mutex, std::defer_lock) { }
374 upgrade_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t) : read_lock(rw_mutex, std::adopt_lock) { }
375 upgrade_lock(const upgrade_lock&) = delete;
376 upgrade_lock(upgrade_lock &&other) = default;
377
379
380 bool owns_read_lock() const { return read_lock::owns_lock(); }
381
382 using read_lock::lock;
383 using read_lock::unlock;
384
391 [[nodiscard]] write_lock upgrade() {
393 if (rw_mutex_.get().upgrade()) {
394 owns_lock_ = false;
395 return write_lock{rw_mutex_, std::adopt_lock}; // we already own the write lock after upgrade
396 }
397 return write_lock{rw_mutex_, std::defer_lock}; // we could not upgrade to a write lock
398 }
399};
400
401}
NOTE: This class is not thread-safe.
void upgrade_lock()
Upgrades an already held read lock to a write lock, after previously requesting to upgrade.
bool writer_active_
As there can be at most one active writer, we track this information with a bool.
uint16_t writers_waiting_
The number of writers waiting to acquire the singular write lock.
void release_read_lock()
Releases a previously acquired read lock.
bool reader_wants_upgrade_
Whether a single reader wants to upgrade to a writer.
uint16_t readers_waiting_
The number of readers waiting to acquire a read lock.
uint16_t readers_active_
The number of currently active readers, i.e.
bool try_request_upgrade()
Try to request an upgrade.
void acquire_read_lock()
Acquires a previously requested read lock.
bool can_acquire_write_lock() const
The mutex is in a state where a writer can acquire a write (exclusive) lock.
bool can_upgrade_lock() const
The mutex is in a state where a reader can claim the write (exclusive) lock.
void request_write_lock()
Inform others that we want to acquire the write lock.
void request_read_lock()
Inform others that we want to acquire a read lock.
bool can_acquire_read_lock() const
The mutex is in a state where a reader can acquire a reader (shared) lock.
Implements a many-readers, single-writer locking concept.
void lock_write()
Acquire the write lock.
std::condition_variable cv_readers_
Used to signal the respective class of clients that the resource has become available.
bool try_lock_read()
Attempts to immediatly claim a read lock.
detail::reader_writer_mutex_internal rw_
Represents the internal state of the reader-writer mutex.
std::condition_variable cv_writers_
void lock_read()
Acquire a read lock.
bool try_lock_write()
Attempts to immediatly claim the exclusive write lock.
bool upgrade()
Tries to upgrade a read lock to the write lock.
std::mutex mutex_
The mutex to guard operations on members of this class.
#define M_unreachable(MSG)
Definition: macro.hpp:146
#define M_insist(...)
Definition: macro.hpp:129
‍mutable namespace
Definition: Backend.hpp:10
and
Definition: enum_ops.hpp:12
STL namespace.
read_lock(read_lock &&other)
bool owns_lock() const
friend void swap(read_lock &first, read_lock &second)
read_lock(reader_writer_mutex &rw_mutex)
read_lock & operator=(read_lock &&other)
read_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t)
std::reference_wrapper< reader_writer_mutex > rw_mutex_
read_lock(const read_lock &)=delete
read_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t)
This is a helper class that helps managing reader and writer locks claimed from reader_writer_mutex.
@ LOCK_WRITE
holds a write (exclusive) lock
@ LOCK_READ
holds a read (shared) lock
void lock_write()
Acquire the write lock.
bool upgrade()
Tries to upgrade a held read lock to the write lock.
reader_writer_lock & operator=(reader_writer_lock &&other)=default
reader_writer_lock(reader_writer_mutex &rw_mutex)
reader_writer_lock(reader_writer_lock &&other)
reader_writer_lock(const reader_writer_lock &)=delete
std::reference_wrapper< reader_writer_mutex > rw_mutex_
void lock_read()
Acquire a read lock.
enum m::reader_writer_lock::@4 LOCK_NONE
upgrade_lock(const upgrade_lock &)=delete
upgrade_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t)
upgrade_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t)
write_lock upgrade()
Attempts to upgrade the read lock to a write lock.
upgrade_lock(upgrade_lock &&other)=default
upgrade_lock(reader_writer_mutex &rw_mutex)
bool owns_read_lock() const
upgrade_lock & operator=(upgrade_lock &&)=default
friend void swap(write_lock &first, write_lock &second)
bool owns_lock() const
std::reference_wrapper< reader_writer_mutex > rw_mutex_
write_lock(reader_writer_mutex &rw_mutex, std::adopt_lock_t)
write_lock(write_lock &&other)
write_lock(const write_lock &)=delete
write_lock(reader_writer_mutex &rw_mutex)
write_lock & operator=(write_lock &&other)
write_lock(reader_writer_mutex &rw_mutex, std::defer_lock_t)