32 Thread support library [thread]

32.5 Mutual exclusion [thread.mutex]

32.5.3 Mutex requirements [thread.mutex.requirements]

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.