This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++14 status.

### 2093. Throws clause of `condition_variable::wait` with predicate

Section: 33.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 (33.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:

"If `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 33.6.7.2 [thread.once.callonce] p4, where it says:

"Throws: `system_error` when an exception is required (33.2.2 [thread.req.exception]), or any exception thrown by `func`."

or in 33.4.5 [thread.thread.this] p6/p9:

"Throws: Nothing if `Clock` satisfies the `TrivialClock` requirements (29.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 29.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 33 [thread]. Therefore, instead of adding wording to each member, I would simply move those requirements from 33.4.5 [thread.thread.this] p6/p9 to a new paragraph in 33.2.4 [thread.req.timing].

As for 33.7.5 [thread.condition.condvarany], the member functions `wait()` 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.

1. Add a new paragraph at the end of 33.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 the `TrivialClock` requirements (29.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 29.7 [time.clock] do not throw exceptions. — end note]

2. Change 33.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 (33.2.4 [thread.req.timing]) specified by `abs_time`.

   > > -5- Synchronization: None.

   > > -6- Throws: timeout-related exceptions (33.2.4 [thread.req.timing]).Nothing if `Clock` satisfies the `TrivialClock` requirements (29.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 29.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 (33.2.4 [thread.req.timing]) specified by `rel_time`.

   > > -8- Synchronization: None.

   > > -9- Throws: timeout-related exceptions (33.2.4 [thread.req.timing]).Nothing if operations of `chrono::duration<Rep, Period>` do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 29.7 [time.clock] do not throw exceptions. — end note]

3. Change 33.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:

   […]

   -5- Effects: The function attempts to obtain ownership of the mutex within the relative timeout (33.2.4 [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]

   […]

   -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 (33.2.4 [thread.req.timing]).Nothing.

   -10- The expression `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 (33.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 (33.2.4 [thread.req.timing]).Nothing.

4. Change 33.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 (33.2.2 [thread.req.exception]), timeout-related exceptions (33.2.4 [thread.req.timing]), or any exception thrown by `pred`.

   > > […]

   >     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 (33.2.2 [thread.req.exception]) or timeout-related exceptions (33.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 if Equivalent to:

   > > >     return wait_until(lock, chrono::steady_clock::now() + rel_time);

   > > […]

   > > -29- Throws: `system_error` when an exception is required (33.2.2 [thread.req.exception]) or timeout-related exceptions (33.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;

   > > -33- Returns: `pred()`

   > > […]

   > > -36- Throws: `std::system_error` when an exception is required (33.2.2 [thread.req.exception]), timeout-related exceptions (33.2.4 [thread.req.timing]), or any exception thrown by `pred`.

   > > […]

   >     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 if Equivalent to:

   > > >     return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));

   > > […]

   > > -42- Returns: `pred()`

   > > […]

   > > -44- Throws: `system_error` when an exception is required (33.2.2 [thread.req.exception]), timeout-related exceptions (33.2.4 [thread.req.timing]), or any exception thrown by `pred`.

   > > […]

5. Change 33.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 (33.2.2 [thread.req.exception]) or any timeout-related exceptions (33.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 if Equivalent to:

   > > >     return wait_until(lock, chrono::steady_clock::now() + rel_time);

   > > […]

   > > -23- Throws: `system_error` when an exception is required (33.2.2 [thread.req.exception]) or any timeout-related exceptions (33.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- Returns: `pred()`[Note: There is no blocking if `pred()` is initially `true`, or if the timeout has already expired. — end note]

   > > -27- [Note: The returned value indicates whether the predicate evaluates to `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 if Equivalent to:

   > > >     return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));

   > > -29- [Note: There is no blocking if `pred()` is initially `true`, 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 to `true` regardless of whether the timeout was triggered. — end note]

   > > -33- Throws: `system_error` when an exception is required (33.2.2 [thread.req.exception]).

   > > -34- Error conditions:
   > > - equivalent error condition from `lock.lock()` or `lock.unlock()`.