32 Thread support library [thread]

32.5 Mutual exclusion [thread.mutex]

This subclause provides mechanisms for mutual exclusion: mutexes, locks, and call once.
These mechanisms ease the production of race-free programs ([intro.multithread]).

32.5.1 Header <mutex> synopsis [mutex.syn]

namespace std {
  class mutex;
  class recursive_mutex;
  class timed_mutex;
  class recursive_timed_mutex;

  struct defer_lock_t { explicit defer_lock_t() = default; };
  struct try_to_lock_t { explicit try_to_lock_t() = default; };
  struct adopt_lock_t { explicit adopt_lock_t() = default; };

  inline constexpr defer_lock_t  defer_lock { };
  inline constexpr try_to_lock_t try_to_lock { };
  inline constexpr adopt_lock_t  adopt_lock { };

  template<class Mutex> class lock_guard;
  template<class... MutexTypes> class scoped_lock;
  template<class Mutex> class unique_lock;

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

  template<class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
  template<class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);

  struct once_flag;

  template<class Callable, class... Args>
    void call_once(once_flag& flag, Callable&& func, Args&&... args);
}

32.5.2 Header <shared_­mutex> synopsis [shared.mutex.syn]

namespace std {
  class shared_mutex;
  class shared_timed_mutex;
  template<class Mutex> class shared_lock;
  template<class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}

32.5.3 Mutex requirements [thread.mutex.requirements]

32.5.3.1 In general [thread.mutex.requirements.general]

A mutex object facilitates protection against data races and allows safe synchronization of data between execution agents.
An execution agent owns a mutex from the time it successfully calls one of the lock functions until it calls unlock.
Mutexes can be either recursive or non-recursive, and can grant simultaneous ownership to one or many execution agents.
Both recursive and non-recursive mutexes are supplied.

32.5.3.2 Mutex types [thread.mutex.requirements.mutex]

The mutex types are the standard library types mutex, recursive_­mutex, timed_­mutex, recursive_­timed_­mutex, shared_­mutex, and shared_­timed_­mutex.
They meet the requirements set out in this subclause.
In this description, m denotes an object of a mutex type.
The mutex types meet the Cpp17Lockable requirements ([thread.req.lockable.req]).
The mutex types meet Cpp17DefaultConstructible and Cpp17Destructible.
If initialization of an object of a mutex type fails, an exception of type system_­error is thrown.
The mutex types are neither copyable nor movable.
The error conditions for error codes, if any, reported by member functions of the mutex types are as follows:
  • resource_­unavailable_­try_­again — if any native handle type manipulated is not available.
  • operation_­not_­permitted — if the thread does not have the privilege to perform the operation.
  • invalid_­argument — if any native handle type manipulated as part of mutex construction is incorrect.
The implementation provides lock and unlock operations, as described below.
For purposes of determining the existence of a data race, these behave as atomic operations ([intro.multithread]).
The lock and unlock operations on a single mutex appears to occur in a single total order.
Note
:
This can be viewed as the modification order of the mutex.
— end note
 ]
Note
:
Construction and destruction of an object of a mutex type need not be thread-safe; other synchronization should be used to ensure that mutex objects are initialized and visible to other threads.
— end note
 ]
The expression m.lock() is well-formed and has the following semantics:
Preconditions: If m is of type mutex, timed_­mutex, shared_­mutex, or shared_­timed_­mutex, the calling thread does not own the mutex.
Effects: Blocks the calling thread until ownership of the mutex can be obtained for the calling thread.
Postconditions: The calling thread owns the mutex.
Return type: void.
Synchronization: Prior unlock() operations on the same object synchronize with ([intro.multithread]) this operation.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if the thread does not have the privilege to perform the operation.
  • resource_­deadlock_­would_­occur — if the implementation detects that a deadlock would occur.
The expression m.try_­lock() is well-formed and has the following semantics:
Preconditions: If m is of type mutex, timed_­mutex, shared_­mutex, or shared_­timed_­mutex, the calling thread does not own the mutex.
Effects: Attempts to obtain ownership of the mutex for the calling thread without blocking.
If ownership is not obtained, there is no effect and try_­lock() immediately returns.
An implementation may fail to obtain the lock even if it is not held by any other thread.
Note
:
This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange ([atomics]).
— end note
 ]
An implementation should ensure that try_­lock() does not consistently return false in the absence of contending mutex acquisitions.
Return type: bool.
Returns: true if ownership of the mutex was obtained for the calling thread, otherwise false.
Synchronization: If try_­lock() returns true, prior unlock() operations on the same object synchronize with this operation.
Note
:
Since lock() does not synchronize with a failed subsequent try_­lock(), the visibility rules are weak enough that little would be known about the state after a failure, even in the absence of spurious failures.
— end note
 ]
Throws: Nothing.
The expression m.unlock() is well-formed and has the following semantics:
Preconditions: The calling thread owns the mutex.
Effects: Releases the calling thread's ownership of the mutex.
Return type: void.
Synchronization: This operation synchronizes with subsequent lock operations that obtain ownership on the same object.
Throws: Nothing.

32.5.3.2.1 Class mutex [thread.mutex.class]

namespace std {
  class mutex {
  public:
    constexpr mutex() noexcept;
    ~mutex();

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

    void lock();
    bool try_lock();
    void unlock();

    using native_handle_type = implementation-defined;          // see [thread.req.native]
    native_handle_type native_handle();                         // see [thread.req.native]
  };
}
The class mutex provides a non-recursive mutex with exclusive ownership semantics.
If one thread owns a mutex object, attempts by another thread to acquire ownership of that object will fail (for try_­lock()) or block (for lock()) until the owning thread has released ownership with a call to unlock().
Note
:
After a thread A has called unlock(), releasing a mutex, it is possible for another thread B to lock the same mutex, observe that it is no longer in use, unlock it, and destroy it, before thread A appears to have returned from its unlock call.
Implementations are required to handle such scenarios correctly, as long as thread A doesn't access the mutex after the unlock call returns.
These cases typically occur when a reference-counted object contains a mutex that is used to protect the reference count.
— end note
 ]
The class mutex meets all of the mutex requirements ([thread.mutex.requirements]).
It is a standard-layout class ([class.prop]).
Note
:
A program can deadlock if the thread that owns a mutex object calls lock() on that object.
If the implementation can detect the deadlock, a resource_­deadlock_­would_­occur error condition might be observed.
— end note
 ]
The behavior of a program is undefined if it destroys a mutex object owned by any thread or a thread terminates while owning a mutex object.

32.5.3.2.2 Class recursive_­mutex [thread.mutex.recursive]

namespace std {
  class recursive_mutex {
  public:
    recursive_mutex();
    ~recursive_mutex();

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

    void lock();
    bool try_lock() noexcept;
    void unlock();

    using native_handle_type = implementation-defined;          // see [thread.req.native]
    native_handle_type native_handle();                         // see [thread.req.native]
  };
}
The class recursive_­mutex provides a recursive mutex with exclusive ownership semantics.
If one thread owns a recursive_­mutex object, attempts by another thread to acquire ownership of that object will fail (for try_­lock()) or block (for lock()) until the first thread has completely released ownership.
The class recursive_­mutex meets all of the mutex requirements ([thread.mutex.requirements]).
It is a standard-layout class ([class.prop]).
A thread that owns a recursive_­mutex object may acquire additional levels of ownership by calling lock() or try_­lock() on that object.
It is unspecified how many levels of ownership may be acquired by a single thread.
If a thread has already acquired the maximum level of ownership for a recursive_­mutex object, additional calls to try_­lock() fail, and additional calls to lock() throw an exception of type system_­error.
A thread shall call unlock() once for each level of ownership acquired by calls to lock() and try_­lock().
Only when all levels of ownership have been released may ownership be acquired by another thread.
The behavior of a program is undefined if:
  • it destroys a recursive_­mutex object owned by any thread or
  • a thread terminates while owning a recursive_­mutex object.

32.5.3.3 Timed mutex types [thread.timedmutex.requirements]

The timed mutex types are the standard library types timed_­mutex, recursive_­timed_­mutex, and shared_­timed_­mutex.
They meet the requirements set out below.
In this description, m denotes an object of a mutex type, rel_­time denotes an object of an instantiation of duration, and abs_­time denotes an object of an instantiation of time_­point.
The timed mutex types meet the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
The expression m.try_­lock_­for(rel_­time) is well-formed and has the following semantics:
Preconditions: If m is of type timed_­mutex or shared_­timed_­mutex, the calling thread does not own the mutex.
Effects: The function attempts to obtain ownership of the mutex within the relative timeout ([thread.req.timing]) specified by rel_­time.
If the time specified by rel_­time is less than or equal to rel_­time.zero(), the function attempts to obtain ownership without blocking (as if by calling try_­lock()).
The function returns within the timeout specified by rel_­time only if it has obtained ownership of the mutex object.
Note
:
As with try_­lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so.
— end note
 ]
Return type: bool.
Returns: true if ownership was obtained, otherwise false.
Synchronization: If try_­lock_­for() returns true, prior unlock() operations on the same object synchronize with this operation.
Throws: Timeout-related exceptions ([thread.req.timing]).
The expression m.try_­lock_­until(abs_­time) is well-formed and has the following semantics:
Preconditions: If m is of type timed_­mutex or shared_­timed_­mutex, the calling thread does not own the mutex.
Effects: The function attempts to obtain ownership of the mutex.
If abs_­time has already passed, the function attempts to obtain ownership without blocking (as if by calling try_­lock()).
The function returns before the absolute timeout ([thread.req.timing]) specified by abs_­time only if it has obtained ownership of the mutex object.
Note
:
As with try_­lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so.
— end note
 ]
Return type: bool.
Returns: true if ownership was obtained, otherwise false.
Synchronization: If try_­lock_­until() returns true, prior unlock() operations on the same object synchronize with this operation.
Throws: Timeout-related exceptions ([thread.req.timing]).

32.5.3.3.1 Class timed_­mutex [thread.timedmutex.class]

namespace std {
  class timed_mutex {
  public:
    timed_mutex();
    ~timed_mutex();

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

    void lock();    // blocking
    bool try_lock();
    template<class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    using native_handle_type = implementation-defined;          // see [thread.req.native]
    native_handle_type native_handle();                         // see [thread.req.native]
  };
}
The class timed_­mutex provides a non-recursive mutex with exclusive ownership semantics.
If one thread owns a timed_­mutex object, attempts by another thread to acquire ownership of that object will fail (for try_­lock()) or block (for lock(), try_­lock_­for(), and try_­lock_­until()) until the owning thread has released ownership with a call to unlock() or the call to try_­lock_­for() or try_­lock_­until() times out (having failed to obtain ownership).
The class timed_­mutex meets all of the timed mutex requirements ([thread.timedmutex.requirements]).
It is a standard-layout class ([class.prop]).
The behavior of a program is undefined if:
  • it destroys a timed_­mutex object owned by any thread,
  • a thread that owns a timed_­mutex object calls lock(), try_­lock(), try_­lock_­for(), or try_­lock_­until() on that object, or
  • a thread terminates while owning a timed_­mutex object.

32.5.3.3.2 Class recursive_­timed_­mutex [thread.timedmutex.recursive]

namespace std {
  class recursive_timed_mutex {
  public:
    recursive_timed_mutex();
    ~recursive_timed_mutex();

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

    void lock();    // blocking
    bool try_lock() noexcept;
    template<class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    using native_handle_type = implementation-defined;          // see [thread.req.native]
    native_handle_type native_handle();                         // see [thread.req.native]
  };
}
The class recursive_­timed_­mutex provides a recursive mutex with exclusive ownership semantics.
If one thread owns a recursive_­timed_­mutex object, attempts by another thread to acquire ownership of that object will fail (for try_­lock()) or block (for lock(), try_­lock_­for(), and try_­lock_­until()) until the owning thread has completely released ownership or the call to try_­lock_­for() or try_­lock_­until() times out (having failed to obtain ownership).
The class recursive_­timed_­mutex meets all of the timed mutex requirements ([thread.timedmutex.requirements]).
It is a standard-layout class ([class.prop]).
A thread that owns a recursive_­timed_­mutex object may acquire additional levels of ownership by calling lock(), try_­lock(), try_­lock_­for(), or try_­lock_­until() on that object.
It is unspecified how many levels of ownership may be acquired by a single thread.
If a thread has already acquired the maximum level of ownership for a recursive_­timed_­mutex object, additional calls to try_­lock(), try_­lock_­for(), or try_­lock_­until() fail, and additional calls to lock() throw an exception of type system_­error.
A thread shall call unlock() once for each level of ownership acquired by calls to lock(), try_­lock(), try_­lock_­for(), and try_­lock_­until().
Only when all levels of ownership have been released may ownership of the object be acquired by another thread.
The behavior of a program is undefined if:
  • it destroys a recursive_­timed_­mutex object owned by any thread, or
  • a thread terminates while owning a recursive_­timed_­mutex object.

32.5.3.4 Shared mutex types [thread.sharedmutex.requirements]

The standard library types shared_­mutex and shared_­timed_­mutex are shared mutex types.
Shared mutex types meet the requirements of mutex types ([thread.mutex.requirements.mutex]) and additionally meet the requirements set out below.
In this description, m denotes an object of a shared mutex type.
In addition to the exclusive lock ownership mode specified in [thread.mutex.requirements.mutex], shared mutex types provide a shared lock ownership mode.
Multiple execution agents can simultaneously hold a shared lock ownership of a shared mutex type.
But no execution agent holds a shared lock while another execution agent holds an exclusive lock on the same shared mutex type, and vice-versa.
The maximum number of execution agents which can share a shared lock on a single shared mutex type is unspecified, but is at least 10000.
If more than the maximum number of execution agents attempt to obtain a shared lock, the excess execution agents block until the number of shared locks are reduced below the maximum amount by other execution agents releasing their shared lock.
The expression m.lock_­shared() is well-formed and has the following semantics:
Preconditions: The calling thread has no ownership of the mutex.
Effects: Blocks the calling thread until shared ownership of the mutex can be obtained for the calling thread.
If an exception is thrown then a shared lock has not been acquired for the current thread.
Postconditions: The calling thread has a shared lock on the mutex.
Return type: void.
Synchronization: Prior unlock() operations on the same object synchronize with ([intro.multithread]) this operation.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if the thread does not have the privilege to perform the operation.
  • resource_­deadlock_­would_­occur — if the implementation detects that a deadlock would occur.
The expression m.unlock_­shared() is well-formed and has the following semantics:
Preconditions: The calling thread holds a shared lock on the mutex.
Effects: Releases a shared lock on the mutex held by the calling thread.
Return type: void.
Synchronization: This operation synchronizes with subsequent lock() operations that obtain ownership on the same object.
Throws: Nothing.
The expression m.try_­lock_­shared() is well-formed and has the following semantics:
Preconditions: The calling thread has no ownership of the mutex.
Effects: Attempts to obtain shared ownership of the mutex for the calling thread without blocking.
If shared ownership is not obtained, there is no effect and try_­lock_­shared() immediately returns.
An implementation may fail to obtain the lock even if it is not held by any other thread.
Return type: bool.
Returns: true if the shared ownership lock was acquired, false otherwise.
Synchronization: If try_­lock_­shared() returns true, prior unlock() operations on the same object synchronize with this operation.
Throws: Nothing.

32.5.3.4.1 Class shared_­mutex [thread.sharedmutex.class]

namespace std {
  class shared_mutex {
  public:
    shared_mutex();
    ~shared_mutex();

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

    // exclusive ownership
    void lock();                // blocking
    bool try_lock();
    void unlock();

    // shared ownership
    void lock_shared();         // blocking
    bool try_lock_shared();
    void unlock_shared();

    using native_handle_type = implementation-defined;          // see [thread.req.native]
    native_handle_type native_handle();                         // see [thread.req.native]
  };
}
The class shared_­mutex provides a non-recursive mutex with shared ownership semantics.
The class shared_­mutex meets all of the shared mutex requirements ([thread.sharedmutex.requirements]).
It is a standard-layout class ([class.prop]).
The behavior of a program is undefined if:
  • it destroys a shared_­mutex object owned by any thread,
  • a thread attempts to recursively gain any ownership of a shared_­mutex, or
  • a thread terminates while possessing any ownership of a shared_­mutex.
shared_­mutex may be a synonym for shared_­timed_­mutex.

32.5.3.5 Shared timed mutex types [thread.sharedtimedmutex.requirements]

The standard library type shared_­timed_­mutex is a shared timed mutex type.
Shared timed mutex types meet the requirements of timed mutex types ([thread.timedmutex.requirements]), shared mutex types ([thread.sharedmutex.requirements]), and additionally meet the requirements set out below.
In this description, m denotes an object of a shared timed mutex type, rel_­type denotes an object of an instantiation of duration, and abs_­time denotes an object of an instantiation of time_­point.
The expression m.try_­lock_­shared_­for(rel_­time) is well-formed and has the following semantics:
Preconditions: The calling thread has no ownership of the mutex.
Effects: Attempts to obtain shared lock ownership for the calling thread within the relative timeout ([thread.req.timing]) specified by rel_­time.
If the time specified by rel_­time is less than or equal to rel_­time.zero(), the function attempts to obtain ownership without blocking (as if by calling try_­lock_­shared()).
The function returns within the timeout specified by rel_­time only if it has obtained shared ownership of the mutex object.
Note
:
As with try_­lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so.
— end note
 ]
If an exception is thrown then a shared lock has not been acquired for the current thread.
Return type: bool.
Returns: true if the shared lock was acquired, false otherwise.
Synchronization: If try_­lock_­shared_­for() returns true, prior unlock() operations on the same object synchronize with ([intro.multithread]) this operation.
Throws: Timeout-related exceptions ([thread.req.timing]).
The expression m.try_­lock_­shared_­until(abs_­time) is well-formed and has the following semantics:
Preconditions: The calling thread has no ownership of the mutex.
Effects: The function attempts to obtain shared ownership of the mutex.
If abs_­time has already passed, the function attempts to obtain shared ownership without blocking (as if by calling try_­lock_­shared()).
The function returns before the absolute timeout ([thread.req.timing]) specified by abs_­time only if it has obtained shared ownership of the mutex object.
Note
:
As with try_­lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so.
— end note
 ]
If an exception is thrown then a shared lock has not been acquired for the current thread.
Return type: bool.
Returns: true if the shared lock was acquired, false otherwise.
Synchronization: If try_­lock_­shared_­until() returns true, prior unlock() operations on the same object synchronize with ([intro.multithread]) this operation.
Throws: Timeout-related exceptions ([thread.req.timing]).

32.5.3.5.1 Class shared_­timed_­mutex [thread.sharedtimedmutex.class]

namespace std {
  class shared_timed_mutex {
  public:
    shared_timed_mutex();
    ~shared_timed_mutex();

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

    // exclusive ownership
    void lock();                // blocking
    bool try_lock();
    template<class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // shared ownership
    void lock_shared();         // blocking
    bool try_lock_shared();
    template<class Rep, class Period>
      bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock_shared();
  };
}
The class shared_­timed_­mutex provides a non-recursive mutex with shared ownership semantics.
The class shared_­timed_­mutex meets all of the shared timed mutex requirements ([thread.sharedtimedmutex.requirements]).
It is a standard-layout class ([class.prop]).
The behavior of a program is undefined if:
  • it destroys a shared_­timed_­mutex object owned by any thread,
  • a thread attempts to recursively gain any ownership of a shared_­timed_­mutex, or
  • a thread terminates while possessing any ownership of a shared_­timed_­mutex.

32.5.4 Locks [thread.lock]

A lock is an object that holds a reference to a lockable object and may unlock the lockable object during the lock's destruction (such as when leaving block scope).
An execution agent may use a lock to aid in managing ownership of a lockable object in an exception safe manner.
A lock is said to own a lockable object if it is currently managing the ownership of that lockable object for an execution agent.
A lock does not manage the lifetime of the lockable object it references.
Note
:
Locks are intended to ease the burden of unlocking the lockable object under both normal and exceptional circumstances.
— end note
 ]
Some lock constructors take tag types which describe what should be done with the lockable object during the lock's construction.
namespace std {
  struct defer_lock_t  { };     // do not acquire ownership of the mutex
  struct try_to_lock_t { };     // try to acquire ownership of the mutex
                                // without blocking
  struct adopt_lock_t  { };     // assume the calling thread has already
                                // obtained mutex ownership and manage it

  inline constexpr defer_lock_t   defer_lock { };
  inline constexpr try_to_lock_t  try_to_lock { };
  inline constexpr adopt_lock_t   adopt_lock { };
}

32.5.4.1 Class template lock_­guard [thread.lock.guard]

namespace std {
  template<class Mutex>
  class lock_guard {
  public:
    using mutex_type = Mutex;

    explicit lock_guard(mutex_type& m);
    lock_guard(mutex_type& m, adopt_lock_t);
    ~lock_guard();

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

  private:
    mutex_type& pm;             // exposition only
  };
}
An object of type lock_­guard controls the ownership of a lockable object within a scope.
A lock_­guard object maintains ownership of a lockable object throughout the lock_­guard object's lifetime.
The behavior of a program is undefined if the lockable object referenced by pm does not exist for the entire lifetime of the lock_­guard object.
The supplied Mutex type shall meet the Cpp17BasicLockable requirements ([thread.req.lockable.basic]).
explicit lock_guard(mutex_type& m);
Preconditions: If mutex_­type is not a recursive mutex, the calling thread does not own the mutex m.
Effects: Initializes pm with m.
Calls m.lock().
lock_guard(mutex_type& m, adopt_lock_t);
Preconditions: The calling thread owns the mutex m.
Effects: Initializes pm with m.
Throws: Nothing.
~lock_guard();
Effects: As if by pm.unlock().

32.5.4.2 Class template scoped_­lock [thread.lock.scoped]

namespace std {
  template<class... MutexTypes>
  class scoped_lock {
  public:
    using mutex_type = Mutex;   // If MutexTypes... consists of the single type Mutex

    explicit scoped_lock(MutexTypes&... m);
    explicit scoped_lock(adopt_lock_t, MutexTypes&... m);
    ~scoped_lock();

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

  private:
    tuple<MutexTypes&...> pm;   // exposition only
  };
}
An object of type scoped_­lock controls the ownership of lockable objects within a scope.
A scoped_­lock object maintains ownership of lockable objects throughout the scoped_­lock object's lifetime.
The behavior of a program is undefined if the lockable objects referenced by pm do not exist for the entire lifetime of the scoped_­lock object.
When sizeof...(MutexTypes) is 1, the supplied Mutex type shall meet the Cpp17BasicLockable requirements ([thread.req.lockable.basic]).
Otherwise, each of the mutex types shall meet the Cpp17Lockable requirements ([thread.req.lockable.req]).
explicit scoped_lock(MutexTypes&... m);
Preconditions: If a MutexTypes type is not a recursive mutex, the calling thread does not own the corresponding mutex element of m.
Effects: Initializes pm with tie(m...).
Then if sizeof...(MutexTypes) is 0, no effects.
Otherwise if sizeof...(MutexTypes) is 1, then m.lock().
Otherwise, lock(m...).
explicit scoped_lock(adopt_lock_t, MutexTypes&... m);
Preconditions: The calling thread owns all the mutexes in m.
Effects: Initializes pm with tie(m...).
Throws: Nothing.
~scoped_lock();
Effects: For all i in [0, sizeof...(MutexTypes)), get<i>(pm).unlock().

32.5.4.3 Class template unique_­lock [thread.lock.unique]

namespace std {
  template<class Mutex>
  class unique_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.unique.cons], construct/copy/destroy
    unique_lock() noexcept;
    explicit unique_lock(mutex_type& m);
    unique_lock(mutex_type& m, defer_lock_t) noexcept;
    unique_lock(mutex_type& m, try_to_lock_t);
    unique_lock(mutex_type& m, adopt_lock_t);
    template<class Clock, class Duration>
      unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
    template<class Rep, class Period>
      unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
    ~unique_lock();

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

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

    // [thread.lock.unique.locking], locking
    void lock();
    bool try_lock();

    template<class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

    void unlock();

    // [thread.lock.unique.mod], modifiers
    void swap(unique_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.unique.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm;             // exposition only
    bool owns;                  // exposition only
  };

  template<class Mutex>
    void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
}
An object of type unique_­lock controls the ownership of a lockable object within a scope.
Ownership of the lockable object may be acquired at construction or after construction, and may be transferred, after acquisition, to another unique_­lock object.
Objects of type unique_­lock are not copyable but are movable.
The behavior of a program is undefined if the contained pointer pm is not null and the lockable object pointed to by pm does not exist for the entire remaining lifetime ([basic.life]) of the unique_­lock object.
The supplied Mutex type shall meet the Cpp17BasicLockable requirements ([thread.req.lockable.basic]).
Note
:
unique_­lock<Mutex> meets the Cpp17BasicLockable requirements.
If Mutex meets the Cpp17Lockable requirements ([thread.req.lockable.req]), unique_­lock<Mutex> also meets the Cpp17Lockable requirements; if Mutex meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]), unique_­lock<Mutex> also meets the Cpp17TimedLockable requirements.
— end note
 ]

32.5.4.3.1 Constructors, destructor, and assignment [thread.lock.unique.cons]

unique_lock() noexcept;
Postconditions: pm == 0 and owns == false.
explicit unique_lock(mutex_type& m);
Preconditions: If mutex_­type is not a recursive mutex the calling thread does not own the mutex.
Effects: Calls m.lock().
Postconditions: pm == addressof(m) and owns == true.
unique_lock(mutex_type& m, defer_lock_t) noexcept;
Postconditions: pm == addressof(m) and owns == false.
unique_lock(mutex_type& m, try_to_lock_t);
Preconditions: The supplied Mutex type meets the Cpp17Lockable requirements ([thread.req.lockable.req]).
If mutex_­type is not a recursive mutex the calling thread does not own the mutex.
Effects: Calls m.try_­lock().
Postconditions: pm == addressof(m) and owns == res, where res is the value returned by the call to m.try_­lock().
unique_lock(mutex_type& m, adopt_lock_t);
Preconditions: The calling thread owns the mutex.
Postconditions: pm == addressof(m) and owns == true.
Throws: Nothing.
template<class Clock, class Duration> unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
Preconditions: If mutex_­type is not a recursive mutex the calling thread does not own the mutex.
The supplied Mutex type meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
Effects: Calls m.try_­lock_­until(abs_­time).
Postconditions: pm == addressof(m) and owns == res, where res is the value returned by the call to m.try_­lock_­until(abs_­time).
template<class Rep, class Period> unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
Preconditions: If mutex_­type is not a recursive mutex the calling thread does not own the mutex.
The supplied Mutex type meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
Effects: Calls m.try_­lock_­for(rel_­time).
Postconditions: pm == addressof(m) and owns == res, where res is the value returned by the call to m.try_­lock_­for(rel_­time).
unique_lock(unique_lock&& u) noexcept;
Postconditions: pm == u_­p.pm and owns == u_­p.owns (where u_­p is the state of u just prior to this construction), u.pm == 0 and u.owns == false.
unique_lock& operator=(unique_lock&& u);
Effects: If owns calls pm->unlock().
Postconditions: pm == u_­p.pm and owns == u_­p.owns (where u_­p is the state of u just prior to this construction), u.pm == 0 and u.owns == false.
Note
:
With a recursive mutex it is possible for both *this and u to own the same mutex before the assignment.
In this case, *this will own the mutex after the assignment and u will not.
— end note
 ]
Throws: Nothing.
~unique_lock();
Effects: If owns calls pm->unlock().

32.5.4.3.2 Locking [thread.lock.unique.locking]

void lock();
Effects: As if by pm->lock().
Postconditions: owns == true.
Throws: Any exception thrown by pm->lock().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
bool try_lock();
Preconditions: The supplied Mutex meets the Cpp17Lockable requirements ([thread.req.lockable.req]).
Effects: As if by pm->try_­lock().
Returns: The value returned by the call to try_­lock().
Postconditions: owns == res, where res is the value returned by the call to try_­lock().
Throws: Any exception thrown by pm->try_­lock().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
template<class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
Preconditions: The supplied Mutex type meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
Effects: As if by pm->try_­lock_­until(abs_­time).
Returns: The value returned by the call to try_­lock_­until(abs_­time).
Postconditions: owns == res, where res is the value returned by the call to try_­lock_­until(abs_­time).
Throws: Any exception thrown by pm->try_­lock_­until().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
template<class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
Preconditions: The supplied Mutex type meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
Effects: As if by pm->try_­lock_­for(rel_­time).
Returns: The value returned by the call to try_­lock_­for(rel_­time).
Postconditions: owns == res, where res is the value returned by the call to try_­lock_­for(rel_­time).
Throws: Any exception thrown by pm->try_­lock_­for().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
void unlock();
Effects: As if by pm->unlock().
Postconditions: owns == false.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if on entry owns is false.

32.5.4.3.3 Modifiers [thread.lock.unique.mod]

void swap(unique_lock& u) noexcept;
Effects: Swaps the data members of *this and u.
mutex_type* release() noexcept;
Returns: The previous value of pm.
Postconditions: pm == 0 and owns == false.
template<class Mutex> void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
Effects: As if by x.swap(y).

32.5.4.3.4 Observers [thread.lock.unique.obs]

bool owns_lock() const noexcept;
Returns: owns.
explicit operator bool() const noexcept;
Returns: owns.
mutex_type *mutex() const noexcept;
Returns: pm.

32.5.4.4 Class template shared_­lock [thread.lock.shared]

namespace std {
  template<class Mutex>
  class shared_lock {
  public:
    using mutex_type = Mutex;

    // [thread.lock.shared.cons], construct/copy/destroy
    shared_lock() noexcept;
    explicit shared_lock(mutex_type& m);        // blocking
    shared_lock(mutex_type& m, defer_lock_t) noexcept;
    shared_lock(mutex_type& m, try_to_lock_t);
    shared_lock(mutex_type& m, adopt_lock_t);
    template<class Clock, class Duration>
      shared_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
    template<class Rep, class Period>
      shared_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
    ~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;

    // [thread.lock.shared.locking], locking
    void lock();                                // blocking
    bool try_lock();
    template<class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    void unlock();

    // [thread.lock.shared.mod], modifiers
    void swap(shared_lock& u) noexcept;
    mutex_type* release() noexcept;

    // [thread.lock.shared.obs], observers
    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

  private:
    mutex_type* pm;                             // exposition only
    bool owns;                                  // exposition only
  };

  template<class Mutex>
    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
}
An object of type shared_­lock controls the shared ownership of a lockable object within a scope.
Shared ownership of the lockable object may be acquired at construction or after construction, and may be transferred, after acquisition, to another shared_­lock object.
Objects of type shared_­lock are not copyable but are movable.
The behavior of a program is undefined if the contained pointer pm is not null and the lockable object pointed to by pm does not exist for the entire remaining lifetime ([basic.life]) of the shared_­lock object.
The supplied Mutex type shall meet the shared mutex requirements ([thread.sharedtimedmutex.requirements]).
Note
:
shared_­lock<Mutex> meets the Cpp17TimedLockable requirements ([thread.req.lockable.timed]).
— end note
 ]

32.5.4.4.1 Constructors, destructor, and assignment [thread.lock.shared.cons]

shared_lock() noexcept;
Postconditions: pm == nullptr and owns == false.
explicit shared_lock(mutex_type& m);
Preconditions: The calling thread does not own the mutex for any ownership mode.
Effects: Calls m.lock_­shared().
Postconditions: pm == addressof(m) and owns == true.
shared_lock(mutex_type& m, defer_lock_t) noexcept;
Postconditions: pm == addressof(m) and owns == false.
shared_lock(mutex_type& m, try_to_lock_t);
Preconditions: The calling thread does not own the mutex for any ownership mode.
Effects: Calls m.try_­lock_­shared().
Postconditions: pm == addressof(m) and owns == res where res is the value returned by the call to m.try_­lock_­shared().
shared_lock(mutex_type& m, adopt_lock_t);
Preconditions: The calling thread has shared ownership of the mutex.
Postconditions: pm == addressof(m) and owns == true.
template<class Clock, class Duration> shared_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
Preconditions: The calling thread does not own the mutex for any ownership mode.
Effects: Calls m.try_­lock_­shared_­until(abs_­time).
Postconditions: pm == addressof(m) and owns == res where res is the value returned by the call to m.try_­lock_­shared_­until(abs_­time).
template<class Rep, class Period> shared_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
Preconditions: The calling thread does not own the mutex for any ownership mode.
Effects: Calls m.try_­lock_­shared_­for(rel_­time).
Postconditions: pm == addressof(m) and owns == res where res is the value returned by the call to m.try_­lock_­shared_­for(rel_­time).
~shared_lock();
Effects: If owns calls pm->unlock_­shared().
shared_lock(shared_lock&& sl) noexcept;
Postconditions: pm == sl_­p.pm and owns == sl_­p.owns (where sl_­p is the state of sl just prior to this construction), sl.pm == nullptr and sl.owns == false.
shared_lock& operator=(shared_lock&& sl) noexcept;
Effects: If owns calls pm->unlock_­shared().
Postconditions: pm == sl_­p.pm and owns == sl_­p.owns (where sl_­p is the state of sl just prior to this assignment), sl.pm == nullptr and sl.owns == false.

32.5.4.4.2 Locking [thread.lock.shared.locking]

void lock();
Effects: As if by pm->lock_­shared().
Postconditions: owns == true.
Throws: Any exception thrown by pm->lock_­shared().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
bool try_lock();
Effects: As if by pm->try_­lock_­shared().
Returns: The value returned by the call to pm->try_­lock_­shared().
Postconditions: owns == res, where res is the value returned by the call to pm->try_­lock_­shared().
Throws: Any exception thrown by pm->try_­lock_­shared().
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
template<class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
Effects: As if by pm->try_­lock_­shared_­until(abs_­time).
Returns: The value returned by the call to pm->try_­lock_­shared_­until(abs_­time).
Postconditions: owns == res, where res is the value returned by the call to pm->try_­lock_­shared_­until(abs_­time).
Throws: Any exception thrown by pm->try_­lock_­shared_­until(abs_­time).
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
template<class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
Effects: As if by pm->try_­lock_­shared_­for(rel_­time).
Returns: The value returned by the call to pm->try_­lock_­shared_­for(rel_­time).
Postconditions: owns == res, where res is the value returned by the call to pm->try_­lock_­shared_­for(rel_­time).
Throws: Any exception thrown by pm->try_­lock_­shared_­for(rel_­time).
system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if pm is nullptr.
  • resource_­deadlock_­would_­occur — if on entry owns is true.
void unlock();
Effects: As if by pm->unlock_­shared().
Postconditions: owns == false.
Throws: system_­error when an exception is required ([thread.req.exception]).
Error conditions:
  • operation_­not_­permitted — if on entry owns is false.

32.5.4.4.3 Modifiers [thread.lock.shared.mod]

void swap(shared_lock& sl) noexcept;
Effects: Swaps the data members of *this and sl.
mutex_type* release() noexcept;
Returns: The previous value of pm.
Postconditions: pm == nullptr and owns == false.
template<class Mutex> void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
Effects: As if by x.swap(y).

32.5.4.4.4 Observers [thread.lock.shared.obs]

bool owns_lock() const noexcept;
Returns: owns.
explicit operator bool() const noexcept;
Returns: owns.
mutex_type* mutex() const noexcept;
Returns: pm.

32.5.5 Generic locking algorithms [thread.lock.algorithm]

template<class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
Preconditions: Each template parameter type meets the Cpp17Lockable requirements.
Note
:
The unique_­lock class template meets these requirements when suitably instantiated.
— end note
 ]
Effects: Calls try_­lock() for each argument in order beginning with the first until all arguments have been processed or a call to try_­lock() fails, either by returning false or by throwing an exception.
If a call to try_­lock() fails, unlock() is called for all prior arguments with no further calls to try_­lock().
Returns: -1 if all calls to try_­lock() returned true, otherwise a zero-based index value that indicates the argument for which try_­lock() returned false.
template<class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);
Preconditions: Each template parameter type meets the Cpp17Lockable requirements.
Note
:
The unique_­lock class template meets these requirements when suitably instantiated.
— end note
 ]
Effects: All arguments are locked via a sequence of calls to lock(), try_­lock(), or unlock() on each argument.
The sequence of calls does not result in deadlock, but is otherwise unspecified.
Note
:
A deadlock avoidance algorithm such as try-and-back-off must be used, but the specific algorithm is not specified to avoid over-constraining implementations.
— end note
 ]
If a call to lock() or try_­lock() throws an exception, unlock() is called for any argument that had been locked by a call to lock() or try_­lock().

32.5.6 Call once [thread.once]

32.5.6.1 Struct once_­flag [thread.once.onceflag]

namespace std {
  struct once_flag {
    constexpr once_flag() noexcept;

    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
  };
}
The class once_­flag is an opaque data structure that call_­once uses to initialize data without causing a data race or deadlock.
constexpr once_flag() noexcept;
Synchronization: The construction of a once_­flag object is not synchronized.
Postconditions: The object's internal state is set to indicate to an invocation of call_­once with the object as its initial argument that no function has been called.

32.5.6.2 Function call_­once [thread.once.callonce]

template<class Callable, class... Args> void call_once(once_flag& flag, Callable&& func, Args&&... args);
Mandates: is_­invocable_­v<Callable, Args...> is true.
Effects: An execution of call_­once that does not call its func is a passive execution.
An execution of call_­once that calls its func is an active execution.
An active execution calls INVOKE(​std​::​forward<Callable>(func), std​::​forward<Args>(args)...).
If such a call to func throws an exception the execution is exceptional, otherwise it is returning.
An exceptional execution propagates the exception to the caller of call_­once.
Among all executions of call_­once for any given once_­flag: at most one is a returning execution; if there is a returning execution, it is the last active execution; and there are passive executions only if there is a returning execution.
Note
:
Passive executions allow other threads to reliably observe the results produced by the earlier returning execution.
— end note
 ]
Synchronization: For any given once_­flag: all active executions occur in a total order; completion of an active execution synchronizes with the start of the next one in this total order; and the returning execution synchronizes with the return from all passive executions.
Throws: system_­error when an exception is required ([thread.req.exception]), or any exception thrown by func.
Example
:
// global flag, regular function
void init();
std::once_flag flag;

void f() {
  std::call_once(flag, init);
}

// function static flag, function object
struct initializer {
  void operator()();
};

void g() {
  static std::once_flag flag2;
  std::call_once(flag2, initializer());
}

// object flag, member function
class information {
  std::once_flag verified;
  void verifier();
public:
  void verify() { std::call_once(verified, &information::verifier, *this); }
};
— end example
 ]