////
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.
