condition_variable::wait
with predicateSection: 32.7.4 [thread.condition.condvar] Status: C++14 Submitter: Alberto Ganesh Barbati Opened: 2011-10-27 Last modified: 2015-10-20
Priority: Not Prioritized
View all other issues in [thread.condition.condvar].
View all issues with C++14 status.
Discussion:
the Throws: clause of condition_variable::wait/wait_xxx
functions that
take a predicate argument is:
Throws:
system_error
when an exception is required (32.2.2 [thread.req.exception]).
If executing the predicate throws an exception, I would expect such exception to propagate unchanged
to the caller, but the throws clause seems to indicate that it gets mutated into a system_error
.
That's because of 16.3.2.4 [structure.specifications]/4:
F
's semantics contains a Throws:, Postconditions:, or Complexity: element, then that supersedes
any occurrences of that element in the code sequence."
Is my interpretation correct? Does it match the intent?
Daniel comments:
I don't think that this interpretation is entirely correct, the wording does not say that
std::system_error
or a derived class must be thrown, it simply is underspecified
in this regard (The extreme interpretation is that the behaviour would be undefined, but
that would be too far reaching I think). We have better wording for this in
32.6.7.2 [thread.once.callonce] p4, where it says:
"Throws: system_error
when an exception is required (32.2.2 [thread.req.exception]),
or any exception thrown by func
."
or in 32.4.5 [thread.thread.this] p6/p9:
"Throws: Nothing if Clock
satisfies the TrivialClock
requirements
(30.3 [time.clock.req]) and operations of Duration
do not throw exceptions.
[ Note: instantiations of time point types and clocks supplied by the implementation
as specified in 30.7 [time.clock] do not throw exceptions. — end note ]"
So, the here discussed Throws elements should add lines along the lines of
"Any exception thrown by operations of pred
."
and similar wording for time-related operations:
"Any exception thrown by operations of Duration
",
"Any exception thrown by operations of chrono::duration<Rep, Period>
"
[2011-11-28: Ganesh comments and suggests wording]
As for the discussion about the exception thrown by the manipulation of time-related objects, I believe the argument applies to all functions declared in 32 [thread]. Therefore, instead of adding wording to each member, I would simply move those requirements from 32.4.5 [thread.thread.this] p6/p9 to a new paragraph in 32.2.4 [thread.req.timing].
As for 32.7.5 [thread.condition.condvarany], the member functionswait()
and
wait_until()
are described only in terms of the Effects: clause (so strictly speaking,
they need no changes), however, wait_for()
is described with a full set of clauses
including Throws: and Error conditions:. Either we should add those clauses to wait/wait_until
with changes similar to the one above, or remove paragraphs 29 to 34 entirely. By the way,
even paragraph 26 could be removed IMHO.
[2012, Kona]
We like the idea behind the proposed resolution.
Modify the first addition to read instead: "Functions that specify a timeout, will throw if an operation on a clock, time point, or time duration throws an exception." In the note near the bottom change "even if the timeout has already expired" to "or if the timeout has already expired". (This is independent, but the original doesn't seem to make sense.) Move to Review.[2012, Portland]
Concurrency move to Ready with slightly ammended wording.
[2013-04-20 Bristol]
Proposed resolution:
This wording is relative to N3337.
Add a new paragraph at the end of 32.2.4 [thread.req.timing]:
[…]
-6- The resolution of timing provided by an implementation depends on both operating system and hardware. The finest resolution provided by an implementation is called the native resolution. -7- Implementation-provided clocks that are used for these functions shall meet theTrivialClock
requirements (30.3 [time.clock.req]). -?- Functions that specify a timeout, will throw if, during the execution of this function, a clock, time point, or time duration throws an exception. [ Note: instantiations of clock, time point and duration types supplied by the implementation as specified in 30.7 [time.clock] do not throw exceptions. — end note]
Change 32.4.5 [thread.thread.this] as indicated:
template <class Clock, class Duration> void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);;-4- Effects: Blocks the calling thread for the absolute timeout (32.2.4 [thread.req.timing]) specified by
-5- Synchronization: None. -6- Throws: timeout-related exceptions (32.2.4 [thread.req.timing]).abs_time
.Nothing ifClock
satisfies theTrivialClock
requirements (30.3 [time.clock.req]) and operations ofDuration
do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 30.7 [time.clock] do not throw exceptions. — end note]
template <class Rep, class Period> void sleep_for(const chrono::duration<Rep, Period>& rel_time);;-7- Effects: Blocks the calling thread for the relative timeout (32.2.4 [thread.req.timing]) specified by
-8- Synchronization: None. -9- Throws: timeout-related exceptions (32.2.4 [thread.req.timing]).rel_time
.Nothing if operations ofchrono::duration<Rep, Period>
do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 30.7 [time.clock] do not throw exceptions. — end note]
Change 32.6.4.3 [thread.timedmutex.requirements] as indicated:
-3- The expression m.try_lock_for(rel_time)
shall be well-formed and have the following semantics:
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]
[…]
-8- Synchronization: If try_lock_for()
returns true
, prior unlock()
operations on the same object
synchronize with (6.9.2 [intro.multithread]) this operation.
-9- Throws: timeout-related exceptions (32.2.4 [thread.req.timing]).m.try_lock_until(abs_time)
shall be well-formed and have the following semantics:
[…]
-12- 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 (32.2.4 [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]
[…]
-15- Synchronization: If try_lock_until()
returns true, prior unlock()
operations on the same object
synchronize with (6.9.2 [intro.multithread]) this operation.
-16- Throws: timeout-related exceptions (32.2.4 [thread.req.timing]).Change 32.7.4 [thread.condition.condvar] as indicated:
template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred);[…]
-15- Effects: Equivalent to:while (!pred()) wait(lock);[…]
-17- Throws:std::system_error
when an exception is required (32.2.2 [thread.req.exception]), timeout-related exceptions (32.2.4 [thread.req.timing]), or any exception thrown bypred
. […]
template <class Clock, class Duration> cv_status wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time);[…]
-23- Throws:system_error
when an exception is required (32.2.2 [thread.req.exception]) or timeout-related exceptions (32.2.4 [thread.req.timing]). […]
template <class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);[…]
-26- Effects:as ifEquivalent to:return wait_until(lock, chrono::steady_clock::now() + rel_time);[…]
-29- Throws:system_error
when an exception is required (32.2.2 [thread.req.exception]) or timeout-related exceptions (32.2.4 [thread.req.timing]). […]
template <class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);[…]
-32- Effects: Equivalent to:while (!pred()) if (wait_until(lock, abs_time) == cv_status::timeout) return pred(); return true;[…] -36- Throws:
-33- Returns:pred()
std::system_error
when an exception is required (32.2.2 [thread.req.exception]), timeout-related exceptions (32.2.4 [thread.req.timing]), or any exception thrown bypred
. […]
template <class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);[…]
-39- Effects:as ifEquivalent to:return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));[…]
-42- Returns:[…] -44- Throws:pred()
system_error
when an exception is required (32.2.2 [thread.req.exception]), timeout-related exceptions (32.2.4 [thread.req.timing]), or any exception thrown bypred
. […]
Change 32.7.5 [thread.condition.condvarany] as indicated:
template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred);-14- Effects: Equivalent to:
while (!pred()) wait(lock);
template <class Lock, class Clock, class Duration> cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);[…]
-18- Throws:system_error
when an exception is required (32.2.2 [thread.req.exception]) or any timeout-related exceptions (32.2.4 [thread.req.timing]). […]
template <class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);[…]
-20- Effects:as ifEquivalent to:return wait_until(lock, chrono::steady_clock::now() + rel_time);[…]
-23- Throws:system_error
when an exception is required (32.2.2 [thread.req.exception]) or any timeout-related exceptions (32.2.4 [thread.req.timing]). […]
template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);-25- Effects: Equivalent to:
while (!pred()) if (wait_until(lock, abs_time) == cv_status::timeout) return pred(); return true;-26-
-27- [Note: The returned value indicates whether the predicate evaluates toReturns:[Note: There is no blocking ifpred()
pred()
is initiallytrue
, or if the timeout has already expired. — end note]true
regardless of whether the timeout was triggered. end note]
template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-28- Effects:
as ifEquivalent to:return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));
-29- [Note: There is no blocking ifpred()
is initiallytrue
, even if the timeout has already expired. — end note]-30- Postcondition:lock
is locked by the calling thread.-31- Returns:pred()
-32- [Note: The returned value indicates whether the predicate evaluates totrue
regardless of whether the timeout was triggered. — end note]-33- Throws:system_error
when an exception is required (32.2.2 [thread.req.exception]).-34- Error conditions:
equivalent error condition fromlock.lock()
orlock.unlock()
.