33 Thread support library [thread]

33.4 Mutual exclusion [thread.mutex]

33.4.3 Mutex requirements [thread.mutex.requirements]

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

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

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 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 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 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 subsequent lock operations that obtain ownership on the same object.

Throws: Nothing.

33.4.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 shall satisfy all of the mutex requirements. It shall be a standard-layout 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.

33.4.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 shall satisfy all of the mutex requirements. It shall be a standard-layout 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.

33.4.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 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, and abs_­time denotes an object of an instantiation of time_­point.

The timed mutex types shall meet the TimedLockable requirements.

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 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 this operation.

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

33.4.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 shall satisfy all of the timed mutex requirements. It shall be a standard-layout 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.

33.4.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 shall satisfy all of the timed mutex 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.

33.4.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 shall meet the requirements of mutex types, 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 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 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 this operation.

Throws: Nothing.

33.4.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 shall satisfy all of the shared mutex 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.

33.4.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 shall meet the requirements of timed mutex types, shared mutex types, 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, and abs_­time denotes an object of an instantiation of 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]).

33.4.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 shall satisfy all of the shared timed mutex 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.