30 Thread support library [thread]

30.4 Mutual exclusion [thread.mutex]

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

Header <mutex> synopsis

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; };

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

  template <class... MutexTypes> class lock_guard;
  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);
}

Header <shared_mutex> synopsis

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;
}

30.4.1 Mutex requirements [thread.mutex.requirements]

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

A mutex object facilitates protection against data races and allows safe synchronization of data between execution agents ([thread.req.lockable]). 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.

30.4.1.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 shall meet the requirements set out in this section. In this description, m denotes an object of a mutex type.

The mutex types shall meet the Lockable requirements ([thread.req.lockable.req]).

The mutex types shall be DefaultConstructible and Destructible. If initialization of an object of a mutex type fails, an exception of type system_error shall be thrown. The mutex types shall not be copyable or movable.

The error conditions for error codes, if any, reported by member functions of the mutex types shall be:

  • 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 shall provide 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 shall appear to occur in a single total order. [ Note: this can be viewed as the modification order ([intro.multithread]) 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() shall be well-formed and have the following semantics:

Requires: 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 shall 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() shall be well-formed and have the following semantics:

Requires: 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 (Clause [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 ([intro.multithread]) 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() shall be well-formed and have the following semantics:

Requires: The calling thread shall own the mutex.

Effects: Releases the calling thread's ownership of the mutex.

Return type: void

Synchronization: This operation synchronizes with ([intro.multithread]) subsequent lock operations that obtain ownership on the same object.

Throws: Nothing.

30.4.1.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 shall satisfy all of the mutex requirements ([thread.mutex.requirements]). It shall be a standard-layout class (Clause [class]).

Note: A program may 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 may 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.

30.4.1.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 shall satisfy all of the mutex requirements ([thread.mutex.requirements]). It shall be a standard-layout class (Clause [class]).

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() shall fail, and additional calls to lock() shall 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.

30.4.1.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 shall 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 ([time.duration]), and abs_time denotes an object of an instantiation of time_point ([time.point]).

The timed mutex types shall meet the TimedLockable requirements ([thread.req.lockable.timed]).

The expression m.try_lock_for(rel_time) shall be well-formed and have the following semantics:

Requires: 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 shall return 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 ([intro.multithread]) this operation.

Throws: Timeout-related exceptions ([thread.req.timing]).

The expression m.try_lock_until(abs_time) shall be well-formed and have the following semantics:

Requires: 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 shall return 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 ([intro.multithread]) this operation.

Throws: Timeout-related exceptions ([thread.req.timing]).

30.4.1.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 shall satisfy all of the timed mutex requirements ([thread.timedmutex.requirements]). It shall be a standard-layout class (Clause [class]).

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.

30.4.1.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 shall satisfy all of the timed mutex requirements ([thread.timedmutex.requirements]). It shall be a standard-layout class (Clause [class]).

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() shall fail, and additional calls to lock() shall 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.

30.4.1.4 Shared mutex types [thread.sharedmutex.requirements]

The standard library types shared_mutex and shared_timed_mutex are shared mutex types. Shared mutex types shall meet the requirements of mutex types ([thread.mutex.requirements.mutex]), and additionally shall 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 shall hold 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 shall be at least 10000. If more than the maximum number of execution agents attempt to obtain a shared lock, the excess execution agents shall 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() shall be well-formed and have the following semantics:

Requires: 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 shall not have 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 shall 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() shall be well-formed and have the following semantics:

Requires: The calling thread shall hold 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 ([intro.multithread]) subsequent lock() operations that obtain ownership on the same object.

Throws: Nothing.

The expression m.try_lock_shared() shall be well-formed and have the following semantics:

Requires: 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 ([intro.multithread]) this operation.

Throws: Nothing.

30.4.1.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 shall satisfy all of the shared mutex requirements ([thread.sharedmutex.requirements]). It shall be a standard-layout class (Clause [class]).

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.

30.4.1.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 shall meet the requirements of timed mutex types ([thread.timedmutex.requirements]), shared mutex types ([thread.sharedmutex.requirements]), and additionally shall 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 ([time.duration]), and abs_time denotes an object of an instantiation of time_point ([time.point]).

The expression m.try_lock_shared_for(rel_time) shall be well-formed and have the following semantics:

Requires: 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 shall return 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 shall not have 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) shall be well-formed and have the following semantics:

Requires: 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 shall return 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 shall not have 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]).

30.4.1.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 shall satisfy all of the shared timed mutex requirements ([thread.sharedtimedmutex.requirements]). It shall be a standard-layout class (Clause [class]).

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.

30.4.2 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

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

30.4.2.1 Class template lock_guard [thread.lock.guard]

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

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

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

  private:
    tuple<MutexTypes&...> pm; // exposition only
  };
}

An object of type lock_guard controls the ownership of lockable objects within a scope. A lock_guard object maintains ownership of lockable objects throughout the lock_guard object's lifetime ([basic.life]). The behavior of a program is undefined if the lockable objects referenced by pm do not exist for the entire lifetime of the lock_guard object. When sizeof...(MutexTypes) is 1, the supplied Mutex type shall meet the BasicLockable requirements. Otherwise, each of the mutex types shall meet the Lockable requirements ([thread.req.lockable.basic]).

explicit lock_guard(MutexTypes&... m);

Requires: 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, then lock(m...).

lock_guard(MutexTypes&... m, adopt_lock_t);

Requires: The calling thread owns all the mutexes in m.

Effects: Initializes pm with tie(m...).

Throws: Nothing.

~lock_guard();

Effects: For all i in [0, sizeof...(MutexTypes)), get<i>(pm).unlock().

30.4.2.2 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 BasicLockable requirements ([thread.req.lockable.basic]).

Note: unique_lock<Mutex> meets the BasicLockable requirements. If Mutex meets the Lockable requirements ([thread.req.lockable.req]), unique_lock<Mutex> also meets the Lockable requirements; if Mutex meets the TimedLockable requirements ([thread.req.lockable.timed]), unique_lock<Mutex> also meets the TimedLockable requirements.  — end note ]

30.4.2.2.1 unique_lock constructors, destructor, and assignment [thread.lock.unique.cons]

unique_lock() noexcept;

Effects: Constructs an object of type unique_lock.

Postconditions: pm == 0 and owns == false.

explicit unique_lock(mutex_type& m);

Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex.

Effects: Constructs an object of type unique_lock and calls m.lock().

Postconditions: pm == addressof(m) and owns == true.

unique_lock(mutex_type& m, defer_lock_t) noexcept;

Effects: Constructs an object of type unique_lock.

Postconditions: pm == addressof(m) and owns == false.

unique_lock(mutex_type& m, try_to_lock_t);

Requires: The supplied Mutex type shall meet the Lockable requirements ([thread.req.lockable.req]). If mutex_type is not a recursive mutex the calling thread does not own the mutex.

Effects: Constructs an object of type unique_lock and 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);

Requires: The calling thread owns the mutex.

Effects: Constructs an object of type unique_lock.

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);

Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied Mutex type shall meet the TimedLockable requirements ([thread.req.lockable.timed]).

Effects: Constructs an object of type unique_lock and 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);

Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied Mutex type shall meet the TimedLockable requirements ([thread.req.lockable.timed]).

Effects: Constructs an object of type unique_lock and 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().

30.4.2.2.2 unique_lock 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();

Requires: The supplied Mutex shall meet the Lockable 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);

Requires: The supplied Mutex type shall meet the TimedLockable 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);

Requires: The supplied Mutex type shall meet the TimedLockable 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_until(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.

30.4.2.2.3 unique_lock 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).

30.4.2.2.4 unique_lock 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

30.4.2.3 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 TimedLockable requirements ([thread.req.lockable.timed]).  — end note ]

30.4.2.3.1 shared_lock constructors, destructor, and assignment [thread.lock.shared.cons]

shared_lock() noexcept;

Effects: Constructs an object of type shared_lock.

Postconditions: pm == nullptr and owns == false.

explicit shared_lock(mutex_type& m);

Requires: The calling thread does not own the mutex for any ownership mode.

Effects: Constructs an object of type shared_lock and calls m.lock_shared().

Postconditions: pm == addressof(m) and owns == true.

shared_lock(mutex_type& m, defer_lock_t) noexcept;

Effects: Constructs an object of type shared_lock.

Postconditions: pm == addressof(m) and owns == false.

shared_lock(mutex_type& m, try_to_lock_t);

Requires: The calling thread does not own the mutex for any ownership mode.

Effects: Constructs an object of type shared_lock and 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);

Requires: The calling thread has shared ownership of the mutex.

Effects: Constructs an object of type shared_lock.

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);

Requires: The calling thread does not own the mutex for any ownership mode.

Effects: Constructs an object of type shared_lock and 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);

Requires: The calling thread does not own the mutex for any ownership mode.

Effects: Constructs an object of type shared_lock and 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.

30.4.2.3.2 shared_lock 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.

30.4.2.3.3 shared_lock 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).

30.4.2.3.4 shared_lock 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.

30.4.3 Generic locking algorithms [thread.lock.algorithm]

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

Requires: Each template parameter type shall meet the Lockable 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() shall be called for all prior arguments and there shall be no further calls to try_lock().

Returns: -1 if all calls to try_lock() returned true, otherwise a 0-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&...);

Requires: Each template parameter type shall meet the Lockable 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 shall 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() shall be called for any argument that had been locked by a call to lock() or try_lock().

30.4.4 Call once [thread.once]

30.4.4.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;

Effects: Constructs an object of type once_flag.

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.

30.4.4.2 Function call_once [thread.once.callonce]

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

Requires: INVOKE( std::forward<Callable>(func), std::forward<Args>(args)...) ([func.require]) shall be a valid expression.

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 shall call 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 shall propagate the exception to the caller of call_once. Among all executions of call_once for any given once_flag: at most one shall be a returning execution; if there is a returning execution, it shall be 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 ([intro.multithread]) 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 ]