mirror of
https://github.com/boostorg/compat.git
synced 2026-01-19 04:02:16 +00:00
Implement shared_lock
Note, as a first pass draft we eschew TimedLockable support
This commit is contained in:
@@ -18,6 +18,7 @@ https://www.boost.org/LICENSE_1_0.txt
|
||||
include::compat/overview.adoc[]
|
||||
include::compat/changelog.adoc[]
|
||||
include::compat/latch.adoc[]
|
||||
include::compat/shared_lock.adoc[]
|
||||
include::compat/copyright.adoc[]
|
||||
|
||||
:leveloffset: -1
|
||||
|
||||
287
doc/compat/shared_lock.adoc
Normal file
287
doc/compat/shared_lock.adoc
Normal file
@@ -0,0 +1,287 @@
|
||||
////
|
||||
Copyright 2023 Christian Mazakas
|
||||
Distributed under the Boost Software License, Version 1.0.
|
||||
https://www.boost.org/LICENSE_1_0.txt
|
||||
////
|
||||
|
||||
[#shared_lock]
|
||||
# <boost/compat/shared_lock.hpp>
|
||||
:idprefix: shared_lock_
|
||||
|
||||
## Description
|
||||
|
||||
The header `<boost/compat/shared_lock.hpp>` implements, in a portable way, the C++14
|
||||
`std::shared_lock` class template.
|
||||
|
||||
The class `shared_lock` is a RAII wrapper that manages locking and unlocking
|
||||
the provided Mutex, provided that it implements https://en.cppreference.com/w/cpp/named_req/SharedLockable[SharedLockable].
|
||||
|
||||
This is the shared analog of https://en.cppreference.com/w/cpp/thread/unique_lock[unique_lock]
|
||||
and calls `lock_shared()` instead of `lock()`.
|
||||
|
||||
## Example
|
||||
|
||||
```cpp
|
||||
#include <boost/compat/shared_lock.hpp>
|
||||
|
||||
shared_mutex m;
|
||||
|
||||
// acquire the lock by calling `m.lock_shared()`
|
||||
// `m.unlock_shared()` is called automatically for us by `guard` now
|
||||
boost::compat::shared_lock<shared_mutex> guard(m);
|
||||
assert(guard.owns_lock());
|
||||
```
|
||||
|
||||
## Synopsis
|
||||
|
||||
```cpp
|
||||
namespace boost {
|
||||
namespace compat {
|
||||
|
||||
template <class Mutex>
|
||||
class shared_lock;
|
||||
|
||||
template <class Mutex>
|
||||
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;
|
||||
|
||||
template <class Mutex>
|
||||
class shared_lock {
|
||||
using mutex_type = Mutex;
|
||||
|
||||
shared_lock() noexcept = default;
|
||||
explicit shared_lock( mutex_type& m );
|
||||
shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;
|
||||
shared_lock( mutex_type& m, std::try_lock_t );
|
||||
shared_lock( mutex_type& m, std::adopt_lock_t );
|
||||
|
||||
~shared_lock();
|
||||
|
||||
shared_lock( const shared_lock& ) = delete;
|
||||
shared_lock& operator=( const shared_lock& ) = delete;
|
||||
|
||||
shared_lock( shared_lock&& u ) noexcept;
|
||||
shared_lock& operator=( shared_lock&& u ) noexcept;
|
||||
|
||||
void lock();
|
||||
bool try_lock();
|
||||
void unlock();
|
||||
|
||||
void swap( shared_lock& u ) noexcept;
|
||||
mutex_type* release() noexcept;
|
||||
|
||||
mutex_type* mutex() const noexcept;
|
||||
|
||||
bool owns_lock() const noexcept;
|
||||
explicit operator bool() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace compat
|
||||
} // namespace boost
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
### Default Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock() noexcept = default;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Postconditions:;; `mutex() == nullptr` and `owns_lock() == false`.
|
||||
|
||||
### Locking Constructor
|
||||
|
||||
```cpp
|
||||
explicit shared_lock( mutex_type& m );
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Calls `m.lock_shared()`.
|
||||
Postconditions:;; `mutex() == std::addressof(m)` and `owns_lock() == true`.
|
||||
|
||||
### Deferred Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Postconditions:;; `mutex() == std::addressof(m)` and `owns_lock() == false`.
|
||||
|
||||
### Try-to-Lock Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock( mutex_type& m, std::try_lock_t );
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Calls `m.try_lock_shared()`.
|
||||
Postconditions:;; `mutex() == std::addressof(m)` and `owns_lock() == res` where
|
||||
`res` is the result of the `m.try_lock_shared()` call.
|
||||
|
||||
### Adopting Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock( mutex_type& m, std::adopt_lock_t );
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Preconditions:;; `m` must be held by a previous call to `m.lock_shared()` or a
|
||||
successful call to `m.try_lock_shared()`.
|
||||
PostConditions:;; `mutex() == std::addressof(m)` and `owns_lock() == true`.
|
||||
|
||||
### Copy Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock( const shared_lock& ) = delete;
|
||||
```
|
||||
|
||||
`shared_lock` is not copyable.
|
||||
|
||||
### Move Constructor
|
||||
|
||||
```cpp
|
||||
shared_lock( shared_lock&& u ) noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Postconditions:;; `mutex() == s.mutex()` and `owns_lock() == s.owns_lock()` where
|
||||
`s` is the state of `u` before move. `u.mutex() == nullptr` and `u.owns_lock() == false`
|
||||
after move.
|
||||
|
||||
## Assignment
|
||||
|
||||
### Copy Assignment
|
||||
|
||||
```cpp
|
||||
shared_lock& operator=( const shared_lock& ) = delete;
|
||||
```
|
||||
|
||||
`shared_lock` is not copyable.
|
||||
|
||||
### Move Assignment
|
||||
|
||||
```cpp
|
||||
shared_lock& operator=( shared_lock&& u ) noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; If `owns_lock() == true`, calls `unlock()`.
|
||||
Postconditions:;; `mutex() == s.mutex()` and `owns_lock() == s.owns_lock()` where
|
||||
`s` is the state of `u` before move. `u.mutex() == nullptr` and `u.owns_lock() == false`
|
||||
after move.
|
||||
|
||||
## Destructor
|
||||
|
||||
```cpp
|
||||
~shared_lock();
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; If `owns_lock() == true`, calls `unlock()`.
|
||||
|
||||
## Member Functions
|
||||
|
||||
### Locking
|
||||
|
||||
#### lock
|
||||
|
||||
```cpp
|
||||
void lock();
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Calls `mutex()\->lock_shared()`.
|
||||
Postconditions:;; `owns_lock() == true`.
|
||||
Throws:;; Any exception caused by `mutex()\->lock_shared()`. `std::system_error`
|
||||
when `mutex() == nullptr` (with `std::errc::operation_not_permitted`) or
|
||||
`owns_lock() == true` (with `std::errc::resource_deadlock_would_occur`).
|
||||
|
||||
#### try_lock
|
||||
|
||||
```cpp
|
||||
bool try_lock();
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Calls `mutex()\->try_lock_shared()`.
|
||||
Postconditions:;; `owns_lock() == res` where `res = mutex()\->try_lock_shared()`.
|
||||
Throws:;; Any exception caused by `mutex()\->try_lock_shared()`. `std::system_error`
|
||||
when `mutex() == nullptr` (with `std::errc::operation_not_permitted`) or
|
||||
`owns_lock() == true` (with `std::errc::resource_deadlock_would_occur`).
|
||||
|
||||
#### unlock
|
||||
|
||||
```cpp
|
||||
void unlock();
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Calls `mutex()\->unlock_shared()`.
|
||||
Postconditions:;; `owns_lock() == false`.
|
||||
Throws:;; `std::system_error` (with `std::errc::operation_not_permitted`) if `owns_lock() == false`.
|
||||
|
||||
|
||||
### Modifiers
|
||||
|
||||
#### swap
|
||||
|
||||
```cpp
|
||||
void swap( shared_lock& u ) noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Swaps the data members of `*this` and `u`.
|
||||
|
||||
#### release
|
||||
|
||||
```cpp
|
||||
mutex_type* release() noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Postconditions:;; `mutex() == nullptr` and `owns_lock() == false`.
|
||||
Returns:;; The previous value of `mutex()`.
|
||||
|
||||
#### Free Function swap
|
||||
|
||||
```cpp
|
||||
template <class Mutex>
|
||||
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Effects:;; Swaps the data members of `x` and `y` via `x.swap(y)`.
|
||||
|
||||
### Observers
|
||||
|
||||
#### mutex
|
||||
|
||||
```cpp
|
||||
mutex_type* mutex() const noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Returns:;; The value of the internal pointer, either `nullptr` or the address of
|
||||
the mutex.
|
||||
|
||||
#### owns_lock
|
||||
|
||||
```cpp
|
||||
bool owns_lock() const noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Returns:;; A boolean indicating whether or not the mutex is locked by the current
|
||||
`shared_lock` instance.
|
||||
|
||||
#### boolean conversion
|
||||
|
||||
```cpp
|
||||
explicit operator bool() const noexcept;
|
||||
```
|
||||
|
||||
[horizontal]
|
||||
Returns:;; A boolean indicating whether or not the mutex is locked by the current
|
||||
`shared_lock` instance.
|
||||
157
include/boost/compat/shared_lock.hpp
Normal file
157
include/boost/compat/shared_lock.hpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright 2023 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#ifndef BOOST_COMPAT_SHARED_LOCK_HPP_INCLUDED
|
||||
#define BOOST_COMPAT_SHARED_LOCK_HPP_INCLUDED
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <system_error>
|
||||
|
||||
namespace boost {
|
||||
namespace compat {
|
||||
|
||||
template <class Mutex>
|
||||
class shared_lock;
|
||||
|
||||
template <class Mutex>
|
||||
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;
|
||||
|
||||
namespace detail {
|
||||
|
||||
BOOST_NORETURN BOOST_NOINLINE inline void
|
||||
throw_system_error( std::errc e, boost::source_location const& loc ) {
|
||||
boost::throw_exception( std::system_error( std::make_error_code( e ) ), loc );
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <class Mutex>
|
||||
class shared_lock {
|
||||
private:
|
||||
Mutex* pm_ = nullptr;
|
||||
bool owns_ = false;
|
||||
|
||||
public:
|
||||
using mutex_type = Mutex;
|
||||
|
||||
shared_lock() noexcept = default;
|
||||
|
||||
explicit shared_lock( mutex_type& m ) : pm_( std::addressof( m ) ) { lock(); }
|
||||
|
||||
shared_lock( mutex_type& m, std::defer_lock_t ) noexcept
|
||||
: pm_( std::addressof( m ) ) {}
|
||||
|
||||
shared_lock( mutex_type& m, std::try_to_lock_t )
|
||||
: pm_( std::addressof( m ) ) {
|
||||
try_lock();
|
||||
}
|
||||
|
||||
shared_lock( mutex_type& m, std::adopt_lock_t )
|
||||
: pm_( std::addressof( m ) ), owns_{ true } {}
|
||||
|
||||
~shared_lock() {
|
||||
if ( owns_ ) {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
shared_lock( const shared_lock& ) = delete;
|
||||
shared_lock& operator=( const shared_lock& ) = delete;
|
||||
|
||||
shared_lock( shared_lock&& u ) noexcept {
|
||||
pm_ = u.pm_;
|
||||
owns_ = u.owns_;
|
||||
|
||||
u.pm_ = nullptr;
|
||||
u.owns_ = false;
|
||||
}
|
||||
|
||||
shared_lock& operator=( shared_lock&& u ) noexcept {
|
||||
if ( this != &u ) {
|
||||
if ( owns_ ) {
|
||||
unlock();
|
||||
}
|
||||
|
||||
pm_ = u.pm_;
|
||||
owns_ = u.owns_;
|
||||
|
||||
u.pm_ = nullptr;
|
||||
u.owns_ = false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if ( !pm_ ) {
|
||||
detail::throw_system_error( std::errc::operation_not_permitted,
|
||||
BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
if ( owns_lock() ) {
|
||||
detail::throw_system_error( std::errc::resource_deadlock_would_occur,
|
||||
BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
pm_->lock_shared();
|
||||
owns_ = true;
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
if ( !pm_ ) {
|
||||
detail::throw_system_error( std::errc::operation_not_permitted,
|
||||
BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
if ( owns_lock() ) {
|
||||
detail::throw_system_error( std::errc::resource_deadlock_would_occur,
|
||||
BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
bool b = pm_->try_lock_shared();
|
||||
owns_ = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if ( !pm_ || !owns_ ) {
|
||||
detail::throw_system_error( std::errc::operation_not_permitted,
|
||||
BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
|
||||
pm_->unlock_shared();
|
||||
owns_ = false;
|
||||
}
|
||||
|
||||
void swap( shared_lock& u ) noexcept {
|
||||
shared_lock tmp = std::move( *this );
|
||||
*this = std::move( u );
|
||||
u = std::move( tmp );
|
||||
}
|
||||
|
||||
mutex_type* release() noexcept {
|
||||
mutex_type* pm = pm_;
|
||||
pm_ = nullptr;
|
||||
owns_ = false;
|
||||
return pm;
|
||||
}
|
||||
|
||||
mutex_type* mutex() const noexcept { return pm_; }
|
||||
|
||||
bool owns_lock() const noexcept { return owns_; }
|
||||
explicit operator bool() const noexcept { return owns_; }
|
||||
};
|
||||
|
||||
template <class Mutex>
|
||||
void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept {
|
||||
x.swap( y );
|
||||
}
|
||||
|
||||
} // namespace compat
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_COMPAT_SHARED_LOCK_HPP_INCLUDED
|
||||
@@ -25,3 +25,5 @@ project
|
||||
run quick.cpp ;
|
||||
|
||||
run latch_test.cpp ;
|
||||
run shared_lock_test.cpp ;
|
||||
run shared_lock_test.cpp : : : <exception-handling>off <toolset>msvc:<define>_HAS_EXCEPTIONS=0 : no_exceptions_shared_lock_test ;
|
||||
|
||||
549
test/shared_lock_test.cpp
Normal file
549
test/shared_lock_test.cpp
Normal file
@@ -0,0 +1,549 @@
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// manually suppress warnings caused by usage of noexcept and the MS STL for our
|
||||
// <exception-handling>off builds
|
||||
#pragma warning( disable : 4530 )
|
||||
#pragma warning( disable : 4577 )
|
||||
|
||||
#endif
|
||||
|
||||
#include <boost/compat/shared_lock.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/lightweight_test_trait.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#define STATIC_ASSERT( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ )
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wself-move"
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
namespace boost {
|
||||
BOOST_NORETURN void throw_exception( std::exception const&,
|
||||
boost::source_location const& ) {
|
||||
std::terminate();
|
||||
}
|
||||
} // namespace boost
|
||||
#endif
|
||||
|
||||
struct dummy_lock {
|
||||
private:
|
||||
int lock_shared_count_ = 0;
|
||||
int unlock_shared_count_ = 0;
|
||||
|
||||
int lock_unique_count_ = 0;
|
||||
int unlock_unique_count_ = 0;
|
||||
|
||||
public:
|
||||
dummy_lock() = default;
|
||||
~dummy_lock() {
|
||||
BOOST_TEST_EQ( lock_shared_count_, unlock_shared_count_ );
|
||||
BOOST_TEST_EQ( lock_unique_count_, unlock_unique_count_ );
|
||||
}
|
||||
|
||||
void lock() {
|
||||
if ( lock_shared_count_ != unlock_shared_count_ ) {
|
||||
boost::compat::detail::throw_system_error(
|
||||
std::errc::resource_deadlock_would_occur, BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
++lock_unique_count_;
|
||||
}
|
||||
|
||||
void unlock() { ++unlock_unique_count_; }
|
||||
|
||||
bool try_lock() {
|
||||
if ( lock_shared_count_ != unlock_shared_count_ ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++lock_unique_count_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lock_shared() {
|
||||
if ( lock_unique_count_ != unlock_unique_count_ ) {
|
||||
boost::compat::detail::throw_system_error(
|
||||
std::errc::resource_deadlock_would_occur, BOOST_CURRENT_LOCATION );
|
||||
}
|
||||
++lock_shared_count_;
|
||||
}
|
||||
|
||||
bool try_lock_shared() {
|
||||
if ( lock_unique_count_ != unlock_unique_count_ ) {
|
||||
return false;
|
||||
}
|
||||
++lock_shared_count_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void unlock_shared() { ++unlock_shared_count_; }
|
||||
|
||||
int shared_lock_count() const noexcept { return lock_shared_count_; }
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
using shared_lock_type = boost::compat::shared_lock<dummy_lock>;
|
||||
|
||||
// verify that our dummy_lock throws when we hope it does
|
||||
void sanity_tests() {
|
||||
dummy_lock sp;
|
||||
sp.lock();
|
||||
BOOST_TEST_THROWS( sp.lock_shared(), std::system_error );
|
||||
sp.unlock();
|
||||
|
||||
sp.lock_shared();
|
||||
BOOST_TEST_THROWS( sp.lock(), std::system_error );
|
||||
sp.unlock_shared();
|
||||
}
|
||||
|
||||
void default_constructor() {
|
||||
// shared_lock() noexcept;
|
||||
|
||||
BOOST_TEST_TRAIT_SAME( typename shared_lock_type::mutex_type, dummy_lock );
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_nothrow_default_constructible<shared_lock_type>));
|
||||
|
||||
shared_lock_type lock;
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
}
|
||||
|
||||
void locking_construtor() {
|
||||
// explicit shared_lock( mutex_type& m );
|
||||
|
||||
dummy_lock sp;
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp );
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void deferred_constructor() {
|
||||
// shared_lock( mutex_type& m, defer_lock_t t ) noexcept;
|
||||
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp, std::defer_lock );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
}
|
||||
|
||||
void try_lock_constructor() {
|
||||
// shared_lock( mutex_type& m, try_to_lock_t t );
|
||||
|
||||
dummy_lock sp;
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp, std::try_to_lock );
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
sp.lock();
|
||||
|
||||
shared_lock_type lock( sp, std::try_to_lock );
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
|
||||
sp.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void adopt_lock_constructor() {
|
||||
// shared_lock(mutex_type& m, adopt_lock_t);
|
||||
|
||||
dummy_lock sp;
|
||||
sp.lock_shared();
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp, std::adopt_lock );
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
sp.lock();
|
||||
sp.unlock();
|
||||
}
|
||||
|
||||
void move_constructor() {
|
||||
BOOST_TEST_TRAIT_TRUE(
|
||||
(std::is_nothrow_move_constructible<shared_lock_type>));
|
||||
|
||||
{
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp );
|
||||
shared_lock_type lock2( std::move( lock ) );
|
||||
BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock2.owns_lock() );
|
||||
BOOST_TEST( lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp, std::defer_lock );
|
||||
shared_lock_type lock2( std::move( lock ) );
|
||||
BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
}
|
||||
|
||||
{
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock;
|
||||
shared_lock_type lock2( std::move( lock ) );
|
||||
BOOST_TEST_EQ( lock2.mutex(), nullptr );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void move_assignment() {
|
||||
BOOST_TEST_TRAIT_TRUE( (std::is_nothrow_move_assignable<shared_lock_type>));
|
||||
|
||||
{
|
||||
// lhs not bound, not locked
|
||||
// rhs not bound, not locked
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
shared_lock_type lock;
|
||||
shared_lock_type lock2;
|
||||
|
||||
lock2 = std::move( lock );
|
||||
BOOST_TEST_EQ( lock2.mutex(), nullptr );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
|
||||
}
|
||||
|
||||
{
|
||||
// lhs not bound, not locked
|
||||
// rhs bound, locked
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp );
|
||||
shared_lock_type lock2;
|
||||
|
||||
lock2 = std::move( lock );
|
||||
BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock2.owns_lock() );
|
||||
BOOST_TEST( lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
// lhs not bound, not locked
|
||||
// rhs bound, not locked
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
shared_lock_type lock( sp, std::defer_lock );
|
||||
shared_lock_type lock2;
|
||||
|
||||
lock2 = std::move( lock );
|
||||
BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
|
||||
}
|
||||
|
||||
{
|
||||
// lhs bound, locked
|
||||
// rhs not bound, not locked
|
||||
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
shared_lock_type lock;
|
||||
shared_lock_type lock2( sp2 );
|
||||
|
||||
lock2 = std::move( lock );
|
||||
|
||||
BOOST_TEST_EQ( lock2.mutex(), nullptr );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
// lhs bound, not locked
|
||||
// rhs not bound, not locked
|
||||
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
shared_lock_type lock;
|
||||
shared_lock_type lock2( sp, std::defer_lock );
|
||||
|
||||
lock2 = std::move( lock );
|
||||
|
||||
BOOST_TEST_EQ( lock2.mutex(), nullptr );
|
||||
BOOST_TEST( !lock2.owns_lock() );
|
||||
BOOST_TEST( !lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
|
||||
}
|
||||
|
||||
{
|
||||
// lhs bound, locked
|
||||
// rhs bound, locked
|
||||
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
shared_lock_type lock( sp );
|
||||
shared_lock_type lock2( sp2 );
|
||||
|
||||
lock2 = std::move( lock );
|
||||
BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock2.owns_lock() );
|
||||
BOOST_TEST( lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
// self-assign, locked
|
||||
|
||||
dummy_lock sp;
|
||||
shared_lock_type lock( sp );
|
||||
|
||||
lock = std::move( lock );
|
||||
BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
void lock() {
|
||||
{
|
||||
dummy_lock sp;
|
||||
shared_lock_type lock( sp, std::defer_lock );
|
||||
|
||||
lock.lock();
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
BOOST_TEST_THROWS( lock.lock(), std::system_error );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
dummy_lock sp;
|
||||
shared_lock_type lock;
|
||||
BOOST_TEST_THROWS( lock.lock(), std::system_error );
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp );
|
||||
lock.unlock();
|
||||
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
|
||||
sp.lock();
|
||||
sp.unlock();
|
||||
|
||||
BOOST_TEST_THROWS( lock.unlock(), std::system_error );
|
||||
}
|
||||
|
||||
void try_lock() {
|
||||
dummy_lock sp;
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp, std::defer_lock );
|
||||
BOOST_TEST( lock.try_lock() );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
|
||||
BOOST_TEST( !sp.try_lock() );
|
||||
|
||||
BOOST_TEST_THROWS( lock.try_lock(), std::system_error );
|
||||
|
||||
lock.unlock();
|
||||
|
||||
sp.lock();
|
||||
|
||||
BOOST_TEST( !lock.try_lock() );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
sp.unlock();
|
||||
}
|
||||
|
||||
{
|
||||
shared_lock_type lock;
|
||||
BOOST_TEST_THROWS( lock.try_lock(), std::system_error );
|
||||
}
|
||||
}
|
||||
|
||||
void swap() {
|
||||
dummy_lock sp, sp2;
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp );
|
||||
shared_lock_type lock2( sp2 );
|
||||
|
||||
STATIC_ASSERT( noexcept( lock.swap( lock2 ) ) );
|
||||
STATIC_ASSERT( noexcept( swap( lock, lock2 ) ) );
|
||||
|
||||
lock.swap( lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), &sp2 );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
|
||||
BOOST_TEST_EQ( lock2.mutex(), &sp );
|
||||
BOOST_TEST( lock2.owns_lock() );
|
||||
BOOST_TEST( lock2 );
|
||||
|
||||
swap( lock, lock2 );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), &sp );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
|
||||
BOOST_TEST_EQ( lock2.mutex(), &sp2 );
|
||||
BOOST_TEST( lock2.owns_lock() );
|
||||
BOOST_TEST( lock2 );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
|
||||
}
|
||||
|
||||
{
|
||||
shared_lock_type lock( sp );
|
||||
lock.swap( lock );
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), &sp );
|
||||
BOOST_TEST( lock.owns_lock() );
|
||||
BOOST_TEST( lock );
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
dummy_lock sp;
|
||||
|
||||
shared_lock_type lock( sp );
|
||||
|
||||
dummy_lock* pm = lock.release();
|
||||
|
||||
BOOST_TEST_EQ( pm, &sp );
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
pm = lock.release();
|
||||
|
||||
BOOST_TEST_EQ( lock.mutex(), nullptr );
|
||||
BOOST_TEST( !lock.owns_lock() );
|
||||
BOOST_TEST( !lock );
|
||||
|
||||
BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
|
||||
sp.unlock_shared();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
sanity_tests();
|
||||
|
||||
default_constructor();
|
||||
locking_construtor();
|
||||
deferred_constructor();
|
||||
try_lock_constructor();
|
||||
adopt_lock_constructor();
|
||||
|
||||
move_constructor();
|
||||
move_assignment();
|
||||
|
||||
lock();
|
||||
unlock();
|
||||
try_lock();
|
||||
|
||||
swap();
|
||||
release();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
Reference in New Issue
Block a user