22 #if ((__cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >= 1900))
23 #include <shared_mutex>
29 #include <condition_variable>
30 #include <system_error>
32 #if (defined(__USE_UNIX98) || defined(__USE_XOPEN2K))
33 #define HAVE_PTHREAD_RWLOCK
36 #ifndef THROW_OR_ABORT
38 # define THROW_OR_ABORT(exception) (throw (exception))
41 # define THROW_OR_ABORT(exception) \
53 template<
class T,
class U = T>
55 exchange(T& obj, U&& new_value)
57 T old_value = std::move(obj);
58 obj = std::forward<U>(new_value);
62 #if defined(HAVE_PTHREAD_RWLOCK)
65 class shared_mutex_pthread {
66 #if defined(PTHREAD_RWLOCK_INITIALIZER)
67 pthread_rwlock_t _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;
70 shared_mutex_pthread() =
default;
71 ~shared_mutex_pthread() =
default;
73 pthread_rwlock_t _M_rwlock;
76 shared_mutex_pthread() {
77 int __ret = pthread_rwlock_init(&_M_rwlock, NULL);
79 THROW_OR_ABORT(std::bad_alloc());
80 else if (__ret == EAGAIN)
81 THROW_OR_ABORT(std::system_error(
82 std::make_error_code(errc::resource_unavailable_try_again)));
83 else if (__ret == EPERM)
84 THROW_OR_ABORT(std::system_error(
85 std::make_error_code(errc::operation_not_permitted)));
90 ~shared_mutex_pthread() {
91 int __ret __attribute((__unused__)) = pthread_rwlock_destroy(&_M_rwlock);
97 shared_mutex_pthread(
const shared_mutex_pthread&) =
delete;
98 shared_mutex_pthread& operator=(
const shared_mutex_pthread&) =
delete;
101 int __ret = pthread_rwlock_wrlock(&_M_rwlock);
102 if (__ret == EDEADLK)
103 THROW_OR_ABORT(std::system_error(
104 std::make_error_code(errc::resource_deadlock_would_occur)));
110 int __ret = pthread_rwlock_trywrlock(&_M_rwlock);
119 int __ret __attribute((__unused__)) = pthread_rwlock_unlock(&_M_rwlock);
133 __ret = pthread_rwlock_rdlock(&_M_rwlock);
134 }
while (__ret == EAGAIN);
135 if (__ret == EDEADLK)
136 THROW_OR_ABORT(std::system_error(
137 std::make_error_code(errc::resource_deadlock_would_occur)));
142 bool try_lock_shared() {
143 int __ret = pthread_rwlock_tryrdlock(&_M_rwlock);
147 if (__ret == EBUSY || __ret == EAGAIN)
154 void unlock_shared() { unlock(); }
156 void* native_handle() {
return &_M_rwlock; }
191 condition_variable _M_gate1;
193 condition_variable _M_gate2;
197 static constexpr
unsigned _S_write_entered =
198 1U << (
sizeof(unsigned) * __CHAR_BIT__ - 1);
199 static constexpr
unsigned _S_max_readers = ~_S_write_entered;
202 bool _M_write_entered()
const {
return _M_state & _S_write_entered; }
205 unsigned _M_readers()
const {
return _M_state & _S_max_readers; }
219 unique_lock<mutex> __lk(_M_mut);
221 _M_gate1.wait(__lk, [=] {
return !_M_write_entered(); });
222 _M_state |= _S_write_entered;
224 _M_gate2.wait(__lk, [=] {
return _M_readers() == 0; });
229 unique_lock<mutex> __lk(_M_mut, try_to_lock);
230 if (__lk.owns_lock() && _M_state == 0) {
231 _M_state = _S_write_entered;
239 lock_guard<mutex> __lk(_M_mut);
240 assert(_M_write_entered());
244 _M_gate1.notify_all();
251 unique_lock<mutex> __lk(_M_mut);
252 _M_gate1.wait(__lk, [=] {
return _M_state < _S_max_readers; });
258 unique_lock<mutex> __lk(_M_mut, try_to_lock);
259 if (!__lk.owns_lock())
261 if (_M_state < _S_max_readers) {
270 lock_guard<mutex> __lk(_M_mut);
271 assert(_M_readers() > 0);
272 auto __prev = _M_state--;
273 if (_M_write_entered()) {
275 if (_M_readers() == 0)
276 _M_gate2.notify_one();
282 if (__prev == _S_max_readers)
283 _M_gate1.notify_one();
331 #if defined(HAVE_PTHREAD_RWLOCK)
332 typedef void* native_handle_type;
333 native_handle_type native_handle() {
return _M_impl.native_handle(); }
336 detail::shared_mutex_pthread _M_impl;
340 detail::shared_mutex_cv _M_impl;
344 template <
typename Mutex>
354 shared_lock() noexcept : _M_pm(
nullptr), _M_owns(false) {}
363 : _M_pm(std::addressof(__m)), _M_owns(true) {
374 : _M_pm(std::addressof(__m)), _M_owns(
false) {}
383 : _M_pm(std::addressof(__m)), _M_owns(__m.try_lock_shared()) {}
392 : _M_pm(std::addressof(__m)), _M_owns(true) {}
394 template <
typename _Clock,
typename _Duration>
404 const chrono::time_point<_Clock, _Duration>& __abs_time)
405 : _M_pm(std::addressof(__m)),
406 _M_owns(__m.try_lock_shared_until(__abs_time)) {}
408 template <
typename _Rep,
typename _Period>
418 const chrono::duration<_Rep, _Period>& __rel_time)
419 : _M_pm(std::addressof(__m)),
420 _M_owns(__m.try_lock_shared_for(__rel_time)) {}
424 _M_pm->unlock_shared();
427 shared_lock(shared_lock
const&) =
delete;
428 shared_lock& operator=(shared_lock
const&) =
delete;
450 _M_pm->lock_shared();
457 return _M_owns = _M_pm->try_lock_shared();
460 template <
typename _Rep,
typename _Period>
472 return _M_owns = _M_pm->try_lock_shared_for(__rel_time);
475 template <
typename _Clock,
typename _Duration>
487 return _M_owns = _M_pm->try_lock_shared_until(__abs_time);
493 THROW_OR_ABORT(std::system_error(
494 std::make_error_code(errc::resource_deadlock_would_occur)));
495 _M_pm->unlock_shared();
505 std::swap(_M_pm, __u._M_pm);
506 std::swap(_M_owns, __u._M_owns);
517 return detail::exchange(_M_pm,
nullptr);
529 explicit operator bool() const noexcept {
return _M_owns; }
540 void _M_lockable()
const {
541 if (_M_pm ==
nullptr)
542 THROW_OR_ABORT(std::system_error(
543 std::make_error_code(errc::operation_not_permitted)));
545 THROW_OR_ABORT(std::system_error(
546 std::make_error_code(errc::resource_deadlock_would_occur)));
553 template <
typename _Mutex>
554 void swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept {
560 #if defined(HAVE_PTHREAD_RWLOCK)
561 #undef HAVE_PTHREAD_RWLOCK
A shared mutex type implemented using std::condition_variable.
Definition: shared_mutex.h:162
void lock()
Takes ownership of the associated mutex.
Definition: shared_mutex.h:218
bool try_lock()
Tries to take ownership of the mutex without blocking.
Definition: shared_mutex.h:228
void lock_shared()
Blocks the calling thread until the thread obtains shared ownership of the mutex.
Definition: shared_mutex.h:250
bool try_lock_shared()
Tries to take shared ownership of the mutex without blocking.
Definition: shared_mutex.h:257
void unlock()
Releases the ownership of the mutex from the calling thread.
Definition: shared_mutex.h:238
void unlock_shared()
Releases the shared ownership of the mutex from the calling thread.
Definition: shared_mutex.h:269
A shared mutex wrapper that supports timed lock operations and non-exclusive sharing by multiple thre...
Definition: shared_mutex.h:349
bool try_lock_for(const chrono::duration< _Rep, _Period > &__rel_time)
Tries to take shared ownership of the mutex and blocks it until the specified time elapses.
Definition: shared_mutex.h:470
bool try_lock()
Tries to take ownership of the mutex without blocking.
Definition: shared_mutex.h:455
void swap(shared_lock &__u) noexcept
Exchanges the data members of two shared_lock instances.
Definition: shared_mutex.h:504
shared_lock(shared_lock &&__sl) noexcept
Creates a shared_lock instance based on the other shared lock.
Definition: shared_mutex.h:435
shared_lock(mutex_type &__m, const chrono::time_point< _Clock, _Duration > &__abs_time)
Creates a shared_lock instance and tries to lock the associated mutex until the specified absolute ti...
Definition: shared_mutex.h:403
shared_lock(mutex_type &__m, adopt_lock_t)
Creates a shared_lock instance and assumes that the calling thread already owns the associated mutex.
Definition: shared_mutex.h:391
void unlock()
Releases the ownership of the mutex from the calling thread.
Definition: shared_mutex.h:491
mutex_type * release() noexcept
Disassociates the mutex without unlocking.
Definition: shared_mutex.h:515
shared_lock(mutex_type &__m, defer_lock_t) noexcept
Creates a shared_lock instance and does not lock the associated mutex.
Definition: shared_mutex.h:373
shared_lock(mutex_type &__m, try_to_lock_t)
Creates a shared_lock instance and tries to lock the associated mutex without blocking.
Definition: shared_mutex.h:382
void lock()
Takes ownership of the associated mutex.
Definition: shared_mutex.h:448
bool try_lock_until(const chrono::time_point< _Clock, _Duration > &__abs_time)
Tries to take shared ownership of the mutex and blocks it until the absolute time has passed.
Definition: shared_mutex.h:485
Mutex mutex_type
A typedef for the mutex type.
Definition: shared_mutex.h:352
bool owns_lock() const noexcept
Checks whether the lock owns its associated mutex.
Definition: shared_mutex.h:526
shared_lock(mutex_type &__m, const chrono::duration< _Rep, _Period > &__rel_time)
Creates a shared_lock instance and tries to lock the associated mutex until the specified duration ha...
Definition: shared_mutex.h:417
mutex_type * mutex() const noexcept
Gets a pointer to the associated mutex.
Definition: shared_mutex.h:537
shared_lock(mutex_type &__m)
Creates a shared_lock instance and locks the associated mutex.
Definition: shared_mutex.h:362
shared_lock & operator=(shared_lock &&__sl) noexcept
The default move assignment operator.
Definition: shared_mutex.h:442
A shared mutex type that can be locked exclusively by one thread or shared non-exclusively by multipl...
Definition: shared_mutex.h:294
bool try_lock()
Tries to take ownership of the mutex without blocking.
Definition: shared_mutex.h:310
bool try_lock_shared()
Tries to take shared ownership of the mutex without blocking.
Definition: shared_mutex.h:326
void lock_shared()
Blocks the calling thread until the thread obtains shared ownership of the mutex.
Definition: shared_mutex.h:319
void lock()
Takes ownership of the associated mutex.
Definition: shared_mutex.h:303
void unlock_shared()
Releases the shared ownership of the mutex from the calling thread.
Definition: shared_mutex.h:329
void unlock()
Releases the ownership of the mutex from the calling thread.
Definition: shared_mutex.h:313