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.

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