# Boost.Compat

C++11 Implementations of Standard Components Added in Later C++ Standards

## Overview

Boost.Compat is a repository of C++11 implementations of standard components added in later C++ standards. Its target audience is Boost library authors whose libraries support a lower C++ standard, but wish to utilize a component added in a subsequent one.

The criteria for inclusion in Boost.Compat are as follows:

- The implementation should be relatively simple and header-only.

- The component must implement the standard functionality exactly, without deviations or extensions. This allows (but does not require) the implementation to be a simple `using` declaration in case the standard component is available.

- The component must not depend on any Boost libraries except Boost.Config, Boost.Assert, or Boost.ThrowException.

- The component must not be a vocabulary type, visible in the library APIs. The user should never see a `boost::compat` type; the use of Compat types should be confined to library implementations.

## Revision History

### Changes in 1.83.0

- Added `latch.hpp`, an implementation of `std::latch` (contributed by Christian Mazakas.)

- Added `shared_lock.hpp`, a (partial) implementation of `std::shared_lock` (contributed by Christian Mazakas.)

## <boost/compat/latch.hpp>

### Description

The header `<boost/compat/latch.hpp>` implements, in a portable way, the C++20 `<latch>` header.

`latch` is a single-use barrier which counts downwards, enabling synchronization between a set of threads. The counter can be manually decremented by any value, but decrementing below zero causes undefined behavior. The maximum number of waiters is specified to be `boost::compat::latch::max()`.

### Example

    std::ptrdiff_t const num_threads = 16;
    boost::compat::latch l(num_threads);

    std::vector<std::thread> threads;
    for (int i = 0; i < num_threads; ++i) {
      threads.emplace_back([&l] {
        // do some preliminary work here...

        // block until all threads have reached this statement
        l.arrive_and_wait();

        // continue with further work...
      });
    }

    for (auto& t: threads) { t.join(); }

### Synopsis

    namespace boost
    {
    namespace compat
    {

    class latch {
      explicit latch(std::ptrdiff_t expected);

      latch(latch const &) = delete;
      latch &operator=(latch const &) = delete;

      ~latch() = default;

      void count_down(std::ptrdiff_t n = 1);
      bool try_wait() const noexcept;
      void wait() const;
      void arrive_and_wait(std::ptrdiff_t n = 1);

      static constexpr std::ptrdiff_t max() noexcept;
    };

    } // namespace compat
    } // namespace boost

### Constructors

#### Counter Constructor

    explicit latch(std::ptrdiff_t expected);

Preconditions: | `expected >= 0 && expected <= boost::compat::latch::max()`.
Effects: | Constructs a latch with an internal counter value of `expected`.

#### Copy Constructor

    latch(latch const &) = delete;

`latch` is not copyable or movable.

### Member Functions

#### count_down

    void count_down(std::ptrdiff_t n = 1);

Preconditions: | `n` must not be larger than the current internal counter’s value.
Effects: | Decrements the internal counter by `n`.

#### try_wait

    bool try_wait() const noexcept;

Returns a boolean indicating whether or not the latch’s internal counter has reached 0 (`true`) or not (`false`).

#### wait

    void wait() const;

Blocks the current thread until the internal counter has reached 0.

#### arrive_and_wait

    void arrive_and_wait(std::ptrdiff_t n = 1);

Preconditions: | `n` must not be larger than the current internal counter’s value.
Effects: | Decrements the internal counter by `n` and if the counter non-zero, blocks the current thread.

#### max

    static constexpr std::ptrdiff_t max() noexcept;

Returns an implementation-defined number representing the maximum amount of waiters. Currently PTRDIFF_MAX.

## <boost/compat/shared_lock.hpp>

### 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 SharedLockable.

This is the shared analog of unique_lock and calls `lock_shared()` instead of `lock()`.

### Example

    #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

    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

    shared_lock() noexcept = default;

Postconditions: | `mutex() == nullptr` and `owns_lock() == false`.

#### Locking Constructor

    explicit shared_lock( mutex_type& m );

Effects: | Calls `m.lock_shared()`.
Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == true`.

#### Deferred Constructor

    shared_lock( mutex_type& m, std::defer_lock_t ) noexcept;

Postconditions: | `mutex() == std::addressof(m)` and `owns_lock() == false`.

#### Try-to-Lock Constructor

    shared_lock( mutex_type& m, std::try_lock_t );

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

    shared_lock( mutex_type& m, std::adopt_lock_t );

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

    shared_lock( const shared_lock& ) = delete;

`shared_lock` is not copyable.

#### Move Constructor

    shared_lock( shared_lock&& u ) noexcept;

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

    shared_lock& operator=( const shared_lock& ) = delete;

`shared_lock` is not copyable.

#### Move Assignment

    shared_lock& operator=( shared_lock&& u ) noexcept;

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

    ~shared_lock();

Effects: | If `owns_lock() == true`, calls `unlock()`.

### Member Functions

#### Locking

##### lock

    void lock();

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

    bool try_lock();

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

    void unlock();

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

    void swap( shared_lock& u ) noexcept;

Effects: | Swaps the data members of `*this` and `u`.

##### release

    mutex_type* release() noexcept;

Postconditions: | `mutex() == nullptr` and `owns_lock() == false`.
Returns: | The previous value of `mutex()`.

##### Free Function swap

    template <class Mutex>
    void swap( shared_lock<Mutex>& x, shared_lock<Mutex>& y ) noexcept;

Effects: | Swaps the data members of `x` and `y` via `x.swap(y)`.

#### Observers

##### mutex

    mutex_type* mutex() const noexcept;

Returns: | The value of the internal pointer, either `nullptr` or the address of the mutex.

##### owns_lock

    bool owns_lock() const noexcept;

Returns: | A boolean indicating whether or not the mutex is locked by the current `shared_lock` instance.

##### boolean conversion

    explicit operator bool() const noexcept;

Returns: | A boolean indicating whether or not the mutex is locked by the current `shared_lock` instance.

## Copyright and License

This documentation is copyright 2023 Peter Dimov and contributors and is distributed under the Boost Software License, Version 1.0.