859. Monotonic Clock is Conditionally Supported?

Section: 33.5 [thread.condition] Status: C++11 Submitter: Pete Becker Opened: 2008-06-23 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [thread.condition].

View all issues with C++11 status.

Discussion:

Related to 958, 959.

N2661 says that there is a class named monotonic_clock. It also says that this name may be a synonym for system_clock, and that it's conditionally supported. So the actual requirement is that it can be monotonic or not, and you can tell by looking at is_monotonic, or it might not exist at all (since it's conditionally supported). Okay, maybe too much flexibility, but so be it.

A problem comes up in the threading specification, where several variants of wait_for explicitly use monotonic_clock::now(). What is the meaning of an effects clause that says

wait_until(lock, chrono::monotonic_clock::now() + rel_time)

when monotonic_clock is not required to exist?

[ San Francisco: ]

Nick: maybe instead of saying that chrono::monotonic_clock is conditionally supported, we could say that it's always there, but not necessarily supported..

Beman: I'd prefer a typedef that identifies the best clock to use for wait_for locks.

Tom: combine the two concepts; create a duration clock type, but keep the is_monotonic test.

Howard: if we create a duration_clock type, is it a typedef or an entirely true type?

There was broad preference for a typedef.

Move to Open. Howard to provide wording to add a typedef for duration_clock and to replace all uses of monotonic_clock in function calls and signatures with duration_clock.

[ Howard notes post-San Francisco: ]

After further thought I do not believe that creating a duration_clock typedef is the best way to proceed. An implementation may not need to use a time_point to implement the wait_for functions.

For example, on POSIX systems sleep_for can be implemented in terms of nanosleep which takes only a duration in terms of nanoseconds. The current working paper does not describe sleep_for in terms of sleep_until. And paragraph 2 of 33.2.4 [thread.req.timing] has the words strongly encouraging implementations to use monotonic clocks for sleep_for:

2 The member functions whose names end in _for take an argument that specifies a relative time. Implementations should use a monotonic clock to measure time for these functions.

I believe the approach taken in describing the effects of sleep_for and try_lock_for is also appropriate for wait_for. I.e. these are not described in terms of their _until variants.

[ 2009-07 Frankfurt: ]

Beman will send some suggested wording changes to Howard.

Move to Ready.

[ 2009-07-21 Beman added the requested wording changes to 962. ]

Proposed resolution:

Change 33.5.3 [thread.condition.condvar], p21-22:

template <class Rep, class Period> 
  bool wait_for(unique_lock<mutex>& lock, 
                const chrono::duration<Rep, Period>& rel_time);

Precondition: lock is locked by the calling thread, and either

21 Effects:

wait_until(lock, chrono::monotonic_clock::now() + rel_time)

Postcondition: lock is locked by the calling thread.

22 Returns: false if the call is returning because the time duration specified by rel_time has elapsed, otherwise true.

[ This part of the wording may conflict with 857 in detail, but does not do so in spirit. If both issues are accepted, there is a logical merge. ]

Throws: std::system_error when the effects or postcondition cannot be achieved.

Error conditions:

Change 33.5.3 [thread.condition.condvar], p26-p29:

template <class Rep, class Period, class Predicate> 
  bool wait_for(unique_lock<mutex>& lock, 
                const chrono::duration<Rep, Period>& rel_time, 
                Predicate pred);

Precondition: lock is locked by the calling thread, and either

26 Effects:

wait_until(lock, chrono::monotonic_clock::now() + rel_time, std::move(pred))
  • Executes a loop: Within the loop the function first evaluates pred() and exits the loop if the result of pred() is true.
  • Atomically calls lock.unlock() and blocks on *this.
  • When unblocked, calls lock.lock() (possibly blocking on the lock).
  • The function will unblock when signaled by a call to notify_one(), a call to notify_all(), by the elapsed time rel_time passing (30.1.4 [thread.req.timing]), or spuriously.
  • If the function exits via an exception, lock.unlock() shall be called prior to exiting the function scope.
  • The loop terminates when pred() returns true or when the time duration specified by rel_time has elapsed.

27 [Note: There is no blocking if pred() is initially true, even if the timeout has already expired. -- end note]

Postcondition: lock is locked by the calling thread.

28 Returns: pred()

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 the effects or postcondition cannot be achieved.

Error conditions:

Change 33.5.4 [thread.condition.condvarany], p18-19:

template <class Lock, class Rep, class Period> 
  bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);

18 Effects:

wait_until(lock, chrono::monotonic_clock::now() + rel_time)

Postcondition: lock is locked by the calling thread.

19 Returns: false if the call is returning because the time duration specified by rel_time has elapsed, otherwise true.

Throws: std::system_error when the returned value, effects, or postcondition cannot be achieved.

Error conditions:

Change 33.5.4 [thread.condition.condvarany], p23-p26:

template <class Lock, class Rep, class Period, class Predicate> 
  bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);

Precondition: lock is locked by the calling thread, and either

23 Effects:

wait_until(lock, chrono::monotonic_clock::now() + rel_time, std::move(pred))
  • Executes a loop: Within the loop the function first evaluates pred() and exits the loop if the result of pred() is true.
  • Atomically calls lock.unlock() and blocks on *this.
  • When unblocked, calls lock.lock() (possibly blocking on the lock).
  • The function will unblock when signaled by a call to notify_one(), a call to notify_all(), by the elapsed time rel_time passing (30.1.4 [thread.req.timing]), or spuriously.
  • If the function exits via an exception, lock.unlock() shall be called prior to exiting the function scope.
  • The loop terminates when pred() returns true or when the time duration specified by rel_time has elapsed.

24 [Note: There is no blocking if pred() is initially true, even if the timeout has already expired. -- end note]

Postcondition: lock is locked by the calling thread.

25 Returns: pred()

26 [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 the effects or postcondition cannot be achieved.

Error conditions: