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

### 1487. Clock related operations exception specifications conflict

Section: 33.4.5 [thread.thread.this] Status: C++11 Submitter: Switzerland Opened: 2010-08-25 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [thread.thread.this].

View all issues with C++11 status.

Discussion:

Addresses CH-25

Clock related operations are currently not required not to throw. So "Throws: Nothing." is not always true.

[ Resolution proposed by ballot comment: ]

> Either require clock related operations not to throw (in 20.10) or change the Throws clauses in 30.3.2. Also possibly add a note that `abs_time` in the past or negative `rel_time` is allowed.

[2011-02-10: Howard Hinnant provides a resolution proposal]

[Previous proposed resolution:]

1. Change the Operational semantics of `C1::now()` in 29.3 [time.clock.req], Table 59 — `Clock` requirements as follows:

   > Table 59 — `Clock` requirements
   > Expression | Return type | Operational semantics
   > --- | --- | ---
   > `C1::now()` | `C1::time_point` | Returns a `time_point` object representing the current point in time. Shall not throw an exception.

[2011-02-19: Daniel comments and suggests an alternative wording]

Imposing the no-throw requirement on `C1::now()` of any clock time is an overly radical step: It has the indirect consequences that representation types for `C1::rep` can never by types with dynamic memory managment, e.g. my `big_int`, which are currently fully supported by the time utilities. Further-on this strong constraint does not even solve the problem described in the issue, because we are still left with the fact that any of the arithmetic operations of `C1::rep`, `C1::duration`, and `C1::time_point` may throw exceptions.

The alternative proposal uses the following strategy: The general `Clock` requirements remain untouched, but we require that any functions of the library-provided clocks from sub-clause 29.7 [time.clock] and their associated types shall not throw exceptions. Second, we replace existing `noexcept` specifications of functions from Clause 30 that depend on durations, clocks, or time points by wording that clarifies that these functions can only throw, if the operations of user-provided durations, clocks, or time points used as arguments to these functions throw exceptions.

[2011-03-23 Daniel and Peter check and simplify the proposed resolution resulting in this paper]

There is an inherent problem with `std::time_point` that it doesn't seem to have an equivalent value for `((time_t)-1)` that gets returned by C's `time()` function to signal a problem, e.g., because the underlying hardware is unavailable. After a lot of thinking and checks we came to the resolution that `timepoint::max()` should be the value to serve as a value signaling errors in cases where we prefer to stick with no-throw conditions. Of-course, user-provided representation types don't need to follow this approach if they prefer exceptions to signal such failures.

the functions `now()` and `from_time_t()` can remain `noexcept` with the solution to return `timepoint::max()` in case the current time cannot be determined or `(time_t)-1` is passed in, respectively.

Based on the previous proposed solution to LWG 1487 we decided that the new `TrivialClock` requirements should define that `now()` mustn't throw and return `timepoint::max()` to signal a problem. That is in line with the C standard where `(time_t)-1` signals a problem. Together with a fix to a - we assumed - buggy specifcation in 20.11.3 p2 which uses "happens-before" relationship with something that isn't any action:

> 2 In Table 59 `C1` and `C2` denote clock types. `t1` and `t2` are values returned by `C1::now()` where the call returning `t1` happens before (1.10) the call returning `t2` and both of these calls happen before `C1::time_point::max()`.

[2011-03-23 Review with Concurrency group suggested further simplifications and Howard pointed out, that we do not need time_point::max() as a special value.]

also the second "happens before" will be changed to "occurs before" in the english meaning. this is to allow a steady clock to wrap.

Peter updates issue accordingly to discussion.

[Note to the editor: we recommend removal of the following redundant paragraphs in 33.7.5 [thread.condition.condvarany] p. 18 to p. 21, p. 27, p. 28, p. 30, and p. 31 that are defining details for the wait functions that are given by the Effects element. ]

[Note to the editor: we recommend removal of the following redundant paragraphs in 33.7.4 [thread.condition.condvar]: p24-p26, p33-p34, and p36-p37 that are defining details for the `wait_for` functions. We believe these paragraphs are redundant with respect to the Effects clauses that define semantics based on `wait_until`. An example of such a specification is the `wait()` with a predicate. ]

Proposed resolution:

1. Change p2 in 20.11.3 [time.clock.req] as follows

   > 2 In Table 59 `C1` and `C2` denote clock types. `t1` and `t2` are values returned by `C1::now()` where the call returning `t1` happens before (1.10) the call returning `t2` and both of these calls happen occur before `C1::time_point::max()`. [ Note: This means `C1` didn't wrap around between `t1` and `t2` — end note ]

2. Add the following new requirement set at the end of sub-clause 29.3 [time.clock.req]: [Comment: This requirement set is intentionally incomplete. The reason for this incompleteness is the based on the fact, that if we would make it right for C++0x, we would end up defining something like a complete `ArithmeticLike` concept for `TC::rep`, `TC::duration`, and `TC::time_point`. But this looks out-of scope for C++0x to me. The effect is that we essentially do not exactly say, which arithmetic or comparison operations can be used in the time-dependent functions from Clause 30, even though I expect that all declared functions of `duration` and `time_point` are well-formed and well-defined. — end comment]

   > 3 [ Note: the relative difference in durations between those reported by a given clock and the SI definition is a measure of the quality of implementation. — end note ]

   > ? A type `TC` meets the `TrivialClock` requirements if:

   > - `TC` satisfies the `Clock` requirements (29.3 [time.clock.req]),

   > - the types `TC::rep`, `TC::duration`, and `TC::time_point` satisfy the requirements of `EqualityComparable` ( [equalitycomparable]), `LessThanComparable` ( [lessthancomparable]), `DefaultConstructible` ( [defaultconstructible]), `CopyConstructible` ( [copyconstructible]), `CopyAssignable` ( [copyassignable]), `Destructible` ( [destructible]), and of numeric types ([numeric.requirements]) [Note: This means in particular, that operations of these types will not throw exceptions — end note ],

   > - lvalues of the types `TC::rep`, `TC::duration`, and `TC::time_point` are swappable (16.4.4.3 [swappable.requirements]),

   > - the function `TC::now()` does not throw exceptions, and

   > - the type `TC::time_point::clock` meets the `TrivialClock` requirements, recursively.

3. Modify 29.7 [time.clock] p. 1 as follows:

   > 1 - The types defined in this subclause shall satisfy the `TrivialClock` requirements (20.11.1).

4. Modify 29.7.2 [time.clock.system] p. 1, class `system_clock` synopsis, as follows:

   >     class system_clock {
   >     public:
   >       typedef see below rep;
   >       typedef ratio<unspecified , unspecified > period;
   >       typedef chrono::duration<rep, period> duration;
   >       typedef chrono::time_point<system_clock> time_point;
   >       static const bool is_monotonic is_steady = unspecified;
   >       static time_point now() noexcept;
   >       // Map to C API
   >       static time_t to_time_t (const time_point& t) noexcept;
   >       static time_point from_time_t(time_t t) noexcept;
   >     };

5. Modify the prototype declarations in 29.7.2 [time.clock.system] p. 3 + p. 4 as indicated (This edit also fixes the miss of the `static` specifier in these prototype declarations):

   >     static time_t to_time_t(const time_point& t) noexcept;

   >     static time_point from_time_t(time_t t) noexcept;

6. Modify 29.7.7 [time.clock.steady] p. 1, class `steady_clock` synopsis, as follows:

   >     class steady_clock {
   >     public:
   >       typedef unspecified rep;
   >       typedef ratio<unspecified , unspecified > period;
   >       typedef chrono::duration<rep, period> duration;
   >       typedef chrono::time_point<unspecified, duration> time_point;
   >       static const bool is_monotonic is_steady = true;

   >       static time_point now() noexcept;
   >     };

7. Modify 29.7.8 [time.clock.hires] p. 1, class `high_resolution_clock` synopsis, as follows:

   >     class high_resolution_clock {
   >     public:
   >       typedef unspecified rep;
   >       typedef ratio<unspecified , unspecified > period;
   >       typedef chrono::duration<rep, period> duration;
   >       typedef chrono::time_point<unspecified, duration> time_point;
   >       static const bool is_monotonic is_steady = unspecified;

   >       static time_point now() noexcept;
   >     };

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

   > ? Implementation-provided clocks that are used for these functions shall meet the `TrivialClock` requirements (29.3 [time.clock.req]).

9. Edit the synopsis of 33.4.5 [thread.thread.this] before p. 1. [Note: this duplicates edits also in D/N3267]:

   >     template <class Clock, class Duration>
   >     void sleep_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
   >     template <class Rep, class Period>
   >     void sleep_for(const chrono::duration<Rep, Period>& rel_time) noexcept;

10. Modify the prototype specifications in 33.4.5 [thread.thread.this] before p. 4 and p. 6 and re-add a Throws element following the Synchronization elements at p. 5 and p. 7:

    >     template <class Clock, class Duration>
    >     void sleep_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;

    > > 4 - [...]

    > > 5 - Synchronization: None.

    > > ? - 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]

    >     template <class Rep, class Period>
    >     void sleep_for(const chrono::duration<Rep, Period>& rel_time) noexcept;

    > > 6 [...]

    > > 7 Synchronization: None.

    > > ? Throws: Nothing if operations of `chrono::duration<Rep, Period>` do not throw exceptions. [Note: Instantiations of duration types supplied by the implementation as specified in 29.7 [time.clock] do not throw exceptions. — end note]

11. Fix a minor incorrectness in p. 5: Duration types need to compare against `duration<>::zero()`, not `0`:

    > 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 (30.2.4) specified by `rel_time`. If the time specified by `rel_time` is less than or equal to `0` `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 ]

12. Modify the class `timed_mutex` synopsis in 33.6.4.3.2 [thread.timedmutex.class] as indicated: [Note: this duplicates edits also in D/N3267]:

    >     class timed_mutex {
    >     public:
    >       [...]
    >       template <class Rep, class Period>
    >         bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept;
    >       template <class Clock, class Duration>
    >         bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
    >       [...]
    >     };

13. Modify the class `recursive_timed_mutex` synopsis in 33.6.4.3.3 [thread.timedmutex.recursive] as indicated: [Note: this duplicates edits also in D/N3267]:

    >     class recursive_timed_mutex {
    >     public:
    >       [...]
    >       template <class Rep, class Period>
    >         bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept;
    >       template <class Clock, class Duration>
    >         bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
    >       [...]
    >     };

14. Modify the class template `unique_lock` synopsis in 33.6.5.4 [thread.lock.unique] as indicated. [Note: this duplicates edits also in D/N3267]:

    >     template <class Mutex>
    >     class unique_lock {
    >     public:
    >       [...]
    >       template <class Clock, class Duration>
    >         unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept;
    >       template <class Rep, class Period>
    >         unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept;
    >       [...]
    >     };

15. Modify the constructor prototypes in 33.6.5.4.2 [thread.lock.unique.cons] before p. 14 and p. 17 [Note: this duplicates edits also in D/N3267]:

    >     template <class Clock, class Duration>
    >       unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept;

    >     template <class Rep, class Period>
    >       unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept;