1268. The Mutex requirements in 30.4.1 and 30.4.2 are wrong

Section: 33.4 [thread.mutex] Status: Resolved Submitter: Anthony Williams Opened: 2009-11-17 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [thread.mutex].

View all issues with Resolved status.

Discussion:

The Mutex requirements in 33.4.3 [thread.mutex.requirements] and 33.4.3.3 [thread.timedmutex.requirements] confuse the requirements on the behaviour of std::mutex et al with the requirements on Lockable types for use with std::unique_lock, std::lock_guard and std::condition_variable_any.

[ 2010 Pittsburgh: ]

Concepts of threads chapter and issue presentation are: Lockable < Mutex < TimedMutex and Lockable < TimedLockable < TimedMutex.

Typo in failed deletion of Mutex in 30.4.4 p4 edits.

Lockable requirements are too weak for condition_variable_any, but the Mutex requirements are too strong.

Need subset of Lockable requirements for condition_variable_any that does not include try_lock. E.g. CvLockable < Lockable.

Text needs updating to recent draft changes.

Needs to specify exception behavior in Lockable.

The current standard is fine for what it says, but it places requirements that are too strong on authors of mutexes and locks.

Move to open status. Suggest Anthony look at condition_variable_any requirements. Suggest Anthony refine requirements/concepts categories.

Related to 964 and 966

[ 2010-03-28 Daniel synced with N3092. ]

[ 2010-10-25 Daniel adds: ]

Accepting n3130 would solve this issue.

[ 2010-11 Batavia: ]

Resolved by adopting n3197.

Proposed resolution:

Add a new section to 33.2 [thread.req] after 33.2.4 [thread.req.timing] as follows:

30.2.5 Requirements for Lockable types

The standard library templates unique_lock (33.4.4.3 [thread.lock.unique]), lock_guard (33.4.4.1 [thread.lock.guard]), lock, try_lock (33.4.5 [thread.lock.algorithm]) and condition_variable_any (33.5.4 [thread.condition.condvarany]) all operate on user-supplied Lockable objects. Such an object must support the member functions specified for either the Lockable Requirements or the TimedLockable requirements as appropriate to acquire or release ownership of a lock by a given thread. [Note: the nature of any lock ownership and any synchronization it may entail are not part of these requirements. — end note]

30.2.5.1 Lockable Requirements

In order to qualify as a Lockable type, the following expressions must be supported, with the specified semantics, where m denotes a value of type L that supports the Lockable:

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

Effects:
Block until a lock can be acquired for the current thread.
Return type:
void

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

Effects:
Attempt to acquire a lock for the current thread without blocking.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

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

Effects:
Release a lock on m held by the current thread.
Return type:
void
Throws:
Nothing if the current thread holds a lock on m.

30.2.5.2 TimedLockable Requirements

For a type to qualify as TimedLockable it must meet the Lockable requirements, and additionally the following expressions must be well-formed, with the specified semantics, where m is an instance of a type TL that supports the TimedLockable requirements, rel_time denotes instantiation of duration (23.17.5 [time.duration]) and abs_time denotes an instantiation of time_point (23.17.6 [time.point])

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

Effects:
Attempt to acquire a lock for the current thread within the specified time period.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

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

Effects:
Attempt to acquire a lock for the current thread before the specified point in time.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

Replace 33.4.3 [thread.mutex.requirements] paragraph 2 with the following:

2 This section describes requirements on template argument types used to instantiate templates defined in the mutex types supplied by the C++ standard library. The template definitions in the C++ standard library refer These types shall conform to the named Mutex requirements whose details are set out below. In this description, m is an object of a Mutex type one of the standard library mutex types std::mutex, std::recursive_mutex, std::timed_mutex or std::recursive_timed_mutex..

Add the following paragraph after 33.4.3 [thread.mutex.requirements] paragraph 2:

A Mutex type shall conform to the Lockable requirements (30.2.5.1).

Replace 33.4.3.3 [thread.timedmutex.requirements] paragraph 1 with the following:

The C++ standard library TimedMutex types std::timed_mutex and std::recursive_timed_mutex A TimedMutex type shall meet the requirements for a Mutex type. In addition, itthey shall meet the requirements set out in this Clause 30.4.2below, where rel_time denotes an instantiation of duration (23.17.5 [time.duration]) and abs_time denotes an instantiation of time_point (23.17.6 [time.point]).

Add the following paragraph after 33.4.3.3 [thread.timedmutex.requirements] paragraph 1:

A TimedMutex type shall conform to the TimedLockable requirements (30.2.5.1).

Add the following paragraph following 33.4.4.1 [thread.lock.guard] paragraph 1:

The supplied Mutex type shall meet the Lockable requirements (30.2.5.1).

Add the following paragraph following 33.4.4.3 [thread.lock.unique] paragraph 1:

The supplied Mutex type shall meet the Lockable requirements (30.2.5.1). unique_lock<Mutex> meets the Lockable requirements. If Mutex meets the TimedLockable requirements (30.2.5.2) then unique_lock<Mutex> also meets the TimedLockable requirements.

Replace the use of "mutex" or "mutex object" with "lockable object" throughout clause 33.4.4 [thread.lock] paragraph 1:

1 A lock is an object that holds a reference to a mutexlockable object and may unlock the mutexlockable object during the lock's destruction (such as when leaving block scope). A thread of execution may use a lock to aid in managing mutex ownership of a lockable object in an exception safe manner. A lock is said to own a mutexlockable object if it is currently managing the ownership of that mutexlockable object for a thread of execution. A lock does not manage the lifetime of the mutexlockable object it references. [ Note: Locks are intended to ease the burden of unlocking the mutexlockable object under both normal and exceptional circumstances. — end note ]

33.4.4 [thread.lock] paragaph 2:

2 Some lock constructors take tag types which describe what should be done with the mutexlockable object during the lock's constuction.

33.4.4.1 [thread.lock.guard] paragaph 1:

1 An object of type lock_guard controls the ownership of a mutexlockable object within a scope. A lock_guard object maintains ownership of a mutexlockable object throughout the lock_guard object's lifetime. The behavior of a program is undefined if the mutexlockable object referenced by pm does not exist for the entire lifetime (3.8) of the lock_guard object. Mutex shall meet the Lockable requirements (30.2.5.1).

33.4.4.3 [thread.lock.unique] paragaph 1:

1 An object of type unique_lock controls the ownership of a mutexlockable object within a scope. Mutex oOwnership 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 mutex pointed to by pm does not exist for the entire remaining lifetime (3.8) of the unique_lock object. Mutex shall meet the Lockable requirements (30.2.5.1).

Add the following to the precondition of unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) in 33.4.4.3.1 [thread.lock.unique.cons] paragraph 18:

template <class Clock, class Duration>
  unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);

18 Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following to the precondition of unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) in 33.4.4.3.1 [thread.lock.unique.cons] paragraph 22

22 Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following as a precondition of bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) before 33.4.4.3.2 [thread.lock.unique.locking] paragraph 8

template <class Clock, class Duration>
  bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);

Requires: The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following as a precondition of bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) before 33.4.4.3.2 [thread.lock.unique.locking] paragraph 12

template <class Rep, class Period>
  bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);

Requires: The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Replace 33.4.5 [thread.lock.algorithm] p1 with the following:

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

1 Requires: Each template parameter type shall meet the Mutex Lockable requirements (30.2.5.1)., except that a call to try_lock() may throw an exception. [Note: The unique_lock class template meets these requirements when suitably instantiated. — end note]

Replace 33.4.5 [thread.lock.algorithm] p4 with the following:

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

4 Requires: Each template parameter type shall meet the MutexMutex Lockable requirements (30.2.5.1)., except that a call to try_lock() may throw an exception. [Note: The unique_lock class template meets these requirements when suitably instantiated. — end note]

Replace 33.5.4 [thread.condition.condvarany] paragraph 1 with:

1 A Lock type shall meet the requirements for a Mutex type Lockable requirements (30.2.5.1), except that try_lock is not required. [Note: All of the standard mutex types meet this requirement. — end note]