Title
lock() postcondition can not be generally achieved
Status
c++11
Section
[thread.condition]
Submitter
Switzerland

Created on 2010-08-25.00:00:00 last changed 154 months ago

Messages

Date: 2011-02-27.00:00:24

Proposed resolution:

  1. Change [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 [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().
Date: 2011-02-27.00:00:00

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

Date: 2010-08-13.00:00:00

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

[thread.condition.condvar], [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:

  • in [thread.condition.condvar] wait with predicate and wait_until with predicate lack the precondition, postcondition and Error conditions sections. the lack of the precondition would allow to call pred() without holding the lock.
  • in [thread.condition.condvar] wait_until and wait_for and [thread.condition.condvarany] wait_for still specify an error condition for a violated precondition. This should be removed.

and add the following proposed solution:

Date: 2010-10-31.20:27:06

[ 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.

Date: 2010-10-24.16:08:33

Addresses CH-30

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

History
Date User Action Args
2011-08-23 20:07:26adminsetstatus: wp -> c++11
2011-04-11 11:23:23adminsetstatus: voting -> wp
2011-03-05 15:24:28adminsetstatus: ready -> voting
2011-02-27 00:00:24adminsetmessages: + msg5562
2010-11-08 22:57:52adminsetmessages: + msg5316
2010-11-08 22:57:52adminsetmessages: + msg5315
2010-11-08 22:57:52adminsetstatus: open -> ready
2010-10-31 20:27:06adminsetmessages: + msg5214
2010-10-24 16:08:33adminsetmessages: + msg5038
2010-08-25 00:00:00admincreate