1497. lock() postcondition can not be generally achieved

Section: 33.5 [thread.condition] Status: C++11 Submitter: Switzerland Opened: 2010-08-25 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [thread.condition].

View all issues with C++11 status.

Discussion:

Addresses CH-30

If lock.lock() throws an exception, the postcondition can not be generally achieved.

[ Resolution proposed by ballot comment: ]

Either state that the postcondition might not be achieved, depending on the error condition, or state that terminate() is called in this case.

[ 2010-08-13 Peter Sommerlad comments and provides wording ]

33.5.3 [thread.condition.condvar], 33.5.4 [thread.condition.condvarany]

p. 13, last bullet, and corresponding paragraphs in all wait functions

Problem:
Condition variable wait might fail, because the lock cannot be acquired when notified. CH-30 says: "If lock.lock() throws an exception, the postcondition can not be generally achieved." CH-30 proposes: "Either state that the postcondition might not be achieved, depending on the error condition, or state that terminate() is called in this case."

The discussion in Rapperswil concluded that calling terminate() might be too drastic in this case and a corresponding exception should be thrown/passed on and one should use a lock type that allows querying its status, which unique_lock allows for std::condition_variable

We also had some additional observations while discussing in Rapperswil:

and add the following proposed solution:

[2011-02-27: Daniel adapts numbering to n3225]

Proposed resolution:

  1. Change 33.5.3 [thread.condition.condvar] as indicated:
    void wait(unique_lock<mutex>& lock);
    

    9 Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait or timed_wait) threads.
    [..]

    11 Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    [..]
    template <class Predicate>
    void wait(unique_lock<mutex>& lock, Predicate pred);
    

    ?? Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait or timed_wait) threads.

    14 Effects:

    while (!pred())
      wait(lock);
    

    ?? Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    ?? Throws: std::system_error when an exception is required (30.2.2).

    ?? Error conditions:

    • equivalent error condition from lock.lock() or lock.unlock().
    template <class Clock, class Duration>
    cv_status wait_until(unique_lock<mutex>& lock,
      const chrono::time_point<Clock, Duration>& abs_time);
    

    15 Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

    [..]

    17 Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    [..]

    20 Error conditions:

    • operation_not_permitted — if the thread does not own the lock.
    • equivalent error condition from lock.lock() or lock.unlock().
    template <class Rep, class Period>
    cv_status wait_for(unique_lock<mutex>& lock,
      const chrono::duration<Rep, Period>& rel_time);
    

    21 Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

    [..]

    24 Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    [..]

    26 Error conditions:

    • operation_not_permitted — if the thread does not own the lock.
    • equivalent error condition from lock.lock() or lock.unlock().
    template <class Clock, class Duration, class Predicate>
    bool wait_until(unique_lock<mutex>& lock,
      const chrono::time_point<Clock, Duration>& abs_time,
        Predicate pred);
    

    ?? Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait or timed_wait) threads.

    27 Effects:

    while (!pred())
      if (wait_until(lock, abs_time) == cv_status::timeout)
        return pred();
    return true;
    

    28 Returns: pred()

    ?? Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    29 [ Note: The returned value indicates whether the predicate evaluates to true regardless of whether the timeout was triggered. — end note ]

    ?? Throws: std::system_error when an exception is required (30.2.2).

    ?? Error conditions:

    • equivalent error condition from lock.lock() or lock.unlock().
    template <class Rep, class Period, class Predicate>
    bool wait_for(unique_lock<mutex>& lock,
      const chrono::duration<Rep, Period>& rel_time,
        Predicate pred);
    

    30 Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

    • no other thread is waiting on this condition_variable object or
    • lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

    [..]

    33 Postcondition: lock.owns_lock() is true and lock.mutex() is locked by the calling thread.

    [..]

    37 Error conditions:

    • operation_not_permitted — if the thread does not own the lock.
    • equivalent error condition from lock.lock() or lock.unlock().
  2. Change 33.5.4 [thread.condition.condvarany] as indicated:

    [..]

    template <class Lock, class Predicate>
    void wait(Lock& lock, Predicate pred);
    

    [Note: if any of the wait functions exits with an exception it is indeterminate if the Lock is held. One can use a Lock type that allows to query that, such as the unique_lock wrapper. — end note]

    11 Effects:

    while (!pred())
      wait(lock);
    

    [..]

    31 Error conditions:

    • operation_not_permitted — if the thread does not own the lock.
    • equivalent error condition from lock.lock() or lock.unlock().