| Document Number: | N3197=10-0187 |
| Date: | 2010-11-12 |
| Author: | Anthony
Williams Just Software Solutions Ltd |
This paper revises N3130.
It provides a proposed resolution
for LWG
issue 1268, and comments GB 138, CH 26, CH27 and US 188 on the
FCD (see N3102). The basic premise of LWG issue 1268 and GB 138 is
that the "Mutex requirements" from the current working draft are
worded as if they are requirements on all lockable types,
including user-defined mutexes and instantiations
of unique_lock. However, the requirements really only
need apply to the standard mutex types such
as std::mutex, and are too strong when applied to
user-defined mutex types. Also, the requirements are worded in
terms of threads, when they could apply equally for other
asynchronous agents.
This paper therefore proposes to separate the existing requirements on the standard mutex types from the general requirements on all lockable types. Note to editor: this paper should be consistent with N3128 under the assumption that they would both be accepted; any inconsistencies are accidental and unintended.
Add a new section to 30.2 [thread.req] after 30.2.4 [thread.req.timing] as follows:
30.2.5 Requirements for Lockable types
An execution agent is an entity, such as a thread, that may perform work in parallel with other execution agents. [Note: Implementations or users may introduce other kinds of agents, such as processes or thread-pool tasks. —end note] The calling agent is determined by context, e.g. the calling thread that contains the call, etc.
[Note: Some lockable objects are “agent-oblivious” in that they work for any execution-agent model because they do not determine or store the agent’s ID (e.g., an ordinary spin-lock). —end note]
The standard library templates
unique_lock(30.4.3.2 [thread.lock.unique]),lock_guard(30.4.3.1 [thread.lock.guard]),lock,try_lock(30.4.4 [thread.lock.algorithm]) andcondition_variable_any(30.5.2 [thread.condition.condvarany]) all operate on user-supplied lockable objects. Such an object must support the member functions specified for either theBasicLockablerequirements, theLockablerequirements or theTimedLockablerequirements as appropriate to acquire or release ownership of alockby a given execution agent. [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 BasicLockable Requirements
In order for a type
Lto qualify as aBasicLockabletype, the following expressions must be supported, with the specified semantics, wheremdenotes a value of typeL:The expression
m.lock()shall be well-formed and have the following semantics:
- Effects:
- Block until a lock can be acquired for the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.
The expression
m.unlock()shall be well-formed and have the following semantics:
- Requires:
- The current execution agent shall hold a lock on
m.- Effects:
- Release a lock on
mheld by the current execution agent.- Throws:
- Nothing.
30.2.5.2 Lockable Requirements
In order for a type
Lto qualify as aLockabletype, it must meet theBasicLockablerequirements. In addition, the following expressions must be supported, with the specified semantics, wheremdenotes a value of typeL:The expression
m.try_lock()shall be well-formed and have the following semantics:
- Effects:
- Attempt to acquire a lock for the current execution agent without blocking. If an exception is thrown then a lock shall not have been acquired for the current execution agent.
- Return type:
bool- Returns:
trueif the lock was acquired,falseotherwise.30.2.5.3
TimedLockableRequirementsFor a type
TLto qualify asTimedLockableit must meet theLockablerequirements, and additionally the following expressions must be well-formed, with the specified semantics, wheremis an instance of a typeTL,rel_timedenotes instantiation ofduration(20.10.3 [time.duration]) andabs_timedenotes an instantiation oftime_point(20.10.4 [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 execution agent within the relative timeout (30.2.4 [thread.req.timing]) specified by
rel_time. The function shall return within the timeout specified byrel_timeonly if it has obtained a lock onmfor the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.- Return type:
bool- Returns:
trueif the lock was acquired,falseotherwise.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 execution agent before the absolute timeout (30.2.4 [thread.req.timing]) specified by
abs_time. The function shall return before the timeout specified byabs_timeonly if it has obtained a lock onmfor the current execution agent. If an exception is thrown then a lock shall not have been acquired for the current execution agent.- Return type:
bool- Returns:
trueif the lock was acquired,falseotherwise.
Replace 30.4.1 [thread.mutex.requirements] paragraph 1 with the following:
1 A mutex object facilitates protection against data races and allows
thread-safe synchronization of data betweenthreadsexecution agents (30.2.5). Athreadexecution agent owns a mutex from the time it successfully calls one of the lock functions until it calls unlock. Mutexes may be either recursive or non-recursive, and may grant simultaneous ownership to one or manythreadsexecution agents. The mutex types supplied by the standard library provide exclusive ownership semantics for threads: only one thread may own the mutex at a time. Both recursive and non-recursive mutexes are supplied.
Replace 30.4.1 [thread.mutex.requirements] paragraph 2 with the following:
2 This section describes requirements on
template argument types used to instantiate templates defined inthe mutex types supplied by the C++ standard library.The template definitions in the C++ standard library referThese types shall conform to thenamedrequirements whose details are set out below. In this description,Mutexmis an object ofaone of the standard library mutex typesMutextypestd::mutex,std::recursive_mutex,std::timed_mutexorstd::recursive_timed_mutex..
Add the following paragraph after 30.4.1 [thread.mutex.requirements] paragraph 2:
A mutex type shall conform to the
Lockablerequirements (30.2.5.2).
Insert a new paragraph before 30.4.1 [thread.mutex.requirements] paragraph 7:
Requires: If
mis of typestd::mutexorstd::timed_mutexthen the calling thread does not own the mutex.
Insert a new paragraph before 30.4.1 [thread.mutex.requirements] paragraph 14:
Requires: If
mis of typestd::mutexorstd::timed_mutexthen the calling thread does not own the mutex.
Replace 30.4.2 [thread.timedmutex.requirements] paragraph 1 with the following:
In addition to the requirements set out in [thread.mutex.requirements] (30.4.1), the C++ standard library types
std::timed_mutexandstd::recursive_timed_mutex(the timed mutex types)Ashall meet the requirements set outTimedMutextype shall meet the requirements for aMutextype. In addition, itin this Clause 30.4.2below, whererel_timedenotes an instantiation ofduration(20.10.3 [time.duration]) andabs_timedenotes an instantiation oftime_point(20.10.4 [time.point]).
Add the following paragraph after 30.4.2 [thread.timedmutex.requirements] paragraph 1:
A timed mutex type shall conform to the
TimedLockablerequirements (30.2.5.3).
Modify 30.4.2 [thread.mutex.requirements] paragraph 3 as follows:
Requires: If the tick period of
rel_timeis not exactly convertible to the native tick period, the duration shall be rounded up to the nearest native tick period. Ifmis of typestd::timed_mutexthen the calling thread does not own the mutex.
Insert a new paragraph before 30.4.2 [thread.mutex.requirements] paragraph 10 as follows:
Requires: If
mis of typestd::timed_mutexthen the calling thread does not own the mutex.
Add the following paragraph following 30.4.3.1 [thread.lock.guard] paragraph 1:
The supplied
Mutextype shall meet theLockablerequirements (30.2.5.2).
Add the following paragraph following 30.4.3.2 [thread.lock.unique] paragraph 1:
The supplied
Mutextype shall meet theLockablerequirements (30.2.5.2).unique_lock<Mutex>meets theLockablerequirements. IfMutexmeets theTimedLockablerequirements (30.2.5.3) thenunique_lock<Mutex>also meets theTimedLockablerequirements.
Replace the use of "mutex" or "mutex object" with "lockable object" throughout clause 30.4.3. 30.4.3 [thread.mutex.locks] paragraph 1:
1 A lock is an object that holds a reference to a
mutexlockable object and may unlock themutexlockable object during the lock’s destruction (such as when leaving block scope). Athread of executionexecution agent may use a lock to aid in managingmutexownership of a lockable object in an exception safe manner. A lock is said to own amutexlockable object if it is currently managing the ownership of thatmutexlockable object for athread of executionexecution agent. A lock does not manage the lifetime of themutexlockable object it references. [ Note: Locks are intended to ease the burden of unlocking themutexlockable object under both normal and exceptional circumstances. — end note ]
30.4.3 [thread.lock] paragraph 2:
2 Some lock constructors take tag types which describe what should be done with the
mutexlockable object during the lock’s constuction.
30.4.3.1 [thread.lock.guard] paragraph 1:
1 An object of type
lock_guardcontrols the ownership of amutexlockable object within a scope. Alock_guardobject maintains ownership of amutexlockable object throughout thelock_guardobject’s lifetime. The behavior of a program is undefined if themutexlockable object referenced bypmdoes not exist for the entire lifetime (3.8) of thelock_guardobject.Mutexshall meet theBasicLockablerequirements (30.2.5.2).
30.4.3.2 [thread.lock.unique] paragraph 1:
1 An object of type
unique_lockcontrols the ownership of amutexlockable object within a scope.MutexoOwnership of the lockable object may be acquired at construction or after construction, and may be transferred, after acquisition, to anotherunique_lockobject. Objects of typeunique_lockare not copyable but are movable. The behavior of a program is undefined if the contained pointerpmis not null and the mutex pointed to bypmdoes not exist for the entire remaining lifetime (3.8) of theunique_lockobject.Mutexshall meet theLockablerequirements (30.2.5.2).
Add the following to the precondition of unique_lock(mutex_type&
m,
const chrono::time_point<Clock, Duration>& abs_time) in
30.4.3.2.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_typeis not a recursive mutex the calling thread does not own the mutex. The suppliedmutex_typetype shall meet theTimedLockablerequirements (30.2.5.3).
Add the following to the precondition of unique_lock(mutex_type&
m,
const chrono::duration<Rep, Period>& rel_time) in
30.4.3.2.1 [thread.lock.unique.cons]
paragraph 22
22 Requires: If
mutex_typeis not a recursive mutex the calling thread does not own the mutex. The suppliedmutex_typetype shall meet theTimedLockablerequirements (30.2.5.3).
Add the following as a precondition of bool try_lock_until(const
chrono::time_point<Clock, Duration>& abs_time) before
30.4.3.2.2 [thread.lock.unique.locking] paragraph 10
template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);Requires: The supplied
mutex_typetype shall meet theTimedLockablerequirements (30.2.5.3).
Add the following as a precondition of bool try_lock_for(const
chrono::duration<Rep, Period>& rel_time) before
30.4.3.2.2 [thread.lock.unique.locking] paragraph 15
template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);Requires: The supplied
mutex_typetype shall meet theTimedLockablerequirements (30.2.5.3).
Replace 30.4.4 [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
requirements (30.2.5.2).MutexLockable, except that a call to[Note: Thetry_lock()may throw an exception.unique_lockclass template meets these requirements when suitably instantiated. — end note]
Replace 30.4.4 [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
requirements (30.2.5.2).MutexLockable, except that a call to[Note: Thetry_lock()may throw an exception.unique_lockclass template meets these requirements when suitably instantiated. — end note]
Replace 30.5.2 [thread.condition.condvarany] paragraph 1 with:
1 A
Locktype shall meet therequirements for aMutextype, except thattry_lockis not requiredBasicLockablerequirements (30.2.5.1). [Note: All of the standard mutex types meet this requirement. If aLocktype other than one of the standard mutex types (or aunique_lockwrapper for a standard mutex type) is used withcondition_variable_anythen the user must ensure that any necessary synchronization is in place with respect to the predicate associated with thecondition_variable_anyinstance. — end note]