C++ Timeout Specification

ISO/IEC JTC1 SC22 WG21 N3128 = 10-0118 - 2010-08-19

Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org

This paper revises FCD comment US 181 and its proposed resolution based on discussions in the August 2010 meeting.

Problem
Solution
Wording
    20.10 Time utilities [time]
    20.10.1 Clock requirements [time.clock.req]
    20.10.5.1 Class system_clock [time.clock.system]
    20.10.5.2 Class monotonic_clock [time.clock.monotonic]
    20.10.5.2 Class steady_clock [time.clock.steady]
    20.10.5.3 Class high_resolution_clock [time.clock.hires]
    30.2.4 Timing specifications [thread.req.timing]
    30.3.2 Namespace this_thread [thread.thread.this]
    30.4.2 TimedMutex requirements [thread.timedmutex.requirements]
    30.5.1 Class condition_variable [thread.condition.condvar]
    30.5.2 Class condition_variable_any [thread.condition.condvarany]
    30.6.6 Class template future [futures.unique_future]
    30.6.7 Class template shared_future [futures.shared_future]
    30.6.8 Class template atomic_future [futures.atomic_future]

Problem

The meaning of clocks and timeouts is poorly defined within the Final Committee Draft when those clocks may be adjusted. Clocks can be adjusted by hours and many network protocols expect responses within seconds. A task regularly scheduled for midnight GMT should execute at midnight even though the clock has been adjusted to eliminate accumulated error. Failure of the standard to be precise about this distinction makes programs effectively unportable.

The root of the problem is that the current definition leaves open disparate implementations that even as implementations increase their quality, separate implementations will not converge on the same behavior.

There will necessarily be some delay in the interrupt response, function return, and scheduling of a thread waking from a timeout. Implementations can reasonably strive to approach zero delay for these activities. So, we call this delay the "quality of implementation".

Separately, there will be some delay due to contention for processor and memory resources. This delay is more under the control of the application programmer and systems administrator than it is under the implementation. So, we call this delay the "quality of management". The tradeoff between resources and responsiveness is necessarily application-dependent.

We can express the actual time of a timeout as the sum of the intended time, the quality of implementation and the quality of management. The remaining problem is to map the given timeout specifications to a common intended time.

If there are no adjustments to the clock time, the intended time may be straightforwardly determined from the manner of specification. In this case, we assume that any difference in durations between reported times and SI units is small, and thus constitutes a measure of the quality of implementation.

The problem arises with the specification of the intended timeout when the clock is adjusted in the middle of the timeout. There are two plausible strategies, the timeout is sensitive to adjustments in the clock time, or it is not. The timeout_until functions have straightforward definitions when they are sensitive to adjustments. The timeout_for functions have straightforward definitions when they are insensitive to adjustments.

Solution

Define timeout_until to respect reported clock time points and define timeout_for to respect real time durations. However, implementation constraints in the short term make a strict definition infeasible. So, instead we encourage such a definition.

A consequence of these definitions is that timeout_until and timeout_for are not functionally redundant. That is, timeout_until(Clock::now()+3_seconds) is generally not equivalent to timeout_for(3_seconds) when the clock is adjusted in the interval.

The implementation of the timeout definition necessarily depends on a steady clock, one that cannot be adjusted. A monotonic clock is not sufficient. While one could be implicit in the standard, below we make one explicit.

Given a steady clock, the monotonic clock seems to be of marginal utility, and we replace the monotonic clock with the steady clock.

[Note that considered the practice of setting clocks forward in unit tests, and believe that continuing to do so even in timeout_for operations would be reasonable because the tests operate in a virtual world, not in the real world. The definition of time in that world need not be the same as the time in the real world.]

Wording

The proposed wording changes are relative to the Final Committee Draft, N3092.

Note to the Editor: This paper should be consistent with N3130 under the assumption that they would both be accepted; any inconsistencies are accidental and unintended.

20.10 Time utilities [time]

Edit within the header synopsis as follows.


// Clocks
class system_clock;
class monotonic_clock;
class steady_clock;
class high_resolution_clock;

20.10.1 Clock requirements [time.clock.req]

Edit within table 56 as follows.

.... .... ....
C1::is_monotonic const bool true if t1 <= t2 is always true, otherwise false. [Note: A clock that can be adjusted backwards is not monotonic. —end note]
C1::is_steady const bool true if the time between clock ticks is constant, otherwise false.
C1::now() C1::time_point Returns a time_point object representing the current point in time.

Add a new paragraph 3.

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

20.10.5.1 Class system_clock [time.clock.system]

Edit paragraph 1 as follows.

Objects of class system_clock represent wall clock time from the system-wide realtime clock.


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 =         unspecified;
  static const bool is_steady =            unspecified;

  static time_point now();

  // Map to C API
  static time_t        to_time_t(const time_point& t);
  static time_point    from_time_t(time_t t);
};

20.10.5.2 Class monotonic_clock [time.clock.monotonic]

20.10.5.2 Class steady_clock [time.clock.steady]

Delete paragraph 1.

Objects of class monotonic_clock represent clocks for which values of time_point never decrease as physical time advances. monotonic_clock may be a synonym for system_clock if system_clock::is_monotonic is true.

Edit paragraph 2 as follows.

The class monotonic_clock is conditionally supported. Objects of class steady_clock represent clocks for which values of time_point advance at a steady rate relative to real time. That is, the clock may not be adjusted.


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

  static time_point now();
};

20.10.5.3 Class high_resolution_clock [time.clock.hires]

Edit paragraph 1 as follows.

Objects of class high_resolution_clock represent clocks with the shortest tick period. high_resolution_clock may be a synonym for system_clock or monotonic_clock steady_clock.


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

   static time_point now();
};

30.2.4 Timing specifications [thread.req.timing]

Add a new paragraph after paragraph 1 as follows.

Implementations necessarily have some delay in returning from a timeout. Any overhead in interrupt response, function return, and scheduling induces a "quality of implementation" delay, expressed as duration Di. Ideally, this delay would be zero. Further, any contention for processor and memory resources induces a "quality of management" delay, expressed as duration Dm. The delay durations may vary from timeout to timeout, but in all cases shorter is better.

Edit paragraph 2 as follows.

The member functions whose names end in _for take an argument that specifies a relative time duration. These functions produce relative timeouts. Implementations should should use a monotonic steady clock to measure time for these functions. [Note: Implementations are not required to use a monotonic clock because such a clock may not be available. —end note] [Footnote: All implementations for which standard time units are meaningful must necessarily have a steady clock within their hardware implementation. —end footnote] Given a duration argument Dt, the real-time duration of the timeout is Dt+Di+Dm.

Add a new paragraph after paragraph 2 as follows.

The member functions whose names end in _until take an argument that specifies a time point. These functions produce absolute timeouts. Implementations should use the clock specified in the time point to measure time for these functions. Given a clock time point argument Ct, the clock time point of the return from timeout should be Ct+Di+Dm when the clock is not adjusted during the timeout. If the clock is adjusted to the time Ca during the timeout, the behavior of functions with an absolute timeout should be as follows.

Implementations shall return from such a timeout at any point from the time specified above to the time it would return from a relative timeout on the difference between Ct and the time point of the call to the _until function.

30.3.2 Namespace this_thread [thread.thread.this]

Edit paragraph 6, regarding sleep_until, as follows.

Effects: Blocks the calling thread at least until the time for the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time.

Edit paragraph 9, regarding sleep_for, as follows.

Effects: Blocks the calling thread for at least the time for the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time.

30.4.2 TimedMutex requirements [thread.timedmutex.requirements]

Edit paragraph 4, regarding try_lock_for, as follows.

Effects: The function attempts to obtain ownership of the mutex within the time relative timeout (30.2.4 [thread.req.timing]) specified by rel_time. If the time specified by rel_time is less than or equal to 0, the function attempts to obtain ownership without blocking (as if by calling try_lock()). The function shall return within the time 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]

Edit paragraph 10, regarding try_lock_until, as follows.

Effects: The function attempts to obtain ownership of the mutex by the time absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time. 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 time timeout 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]

30.5.1 Class condition_variable [thread.condition.condvar]

Edit within paragraph 19, regarding wait_until, as follows.

Effects:

Edit paragraph 21, regarding wait_until, as follows.

Returns: cv_status::timeout if the function unblocked because abs_time was reached the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time expired, otherwise cv_status::no_timeout.

Edit within paragraph 25, regarding wait_for, as follows.

Effects:

Edit paragraph 26, regarding wait_for, as follows.

Returns: cv_status::timeout if the function unblocked because rel_time elapsed the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time expired, otherwise cv_status::no_timeout.

Edit within paragraph 34, regarding the predicate wait_for, as follows.

Effects:

30.5.2 Class condition_variable_any [thread.condition.condvarany]

Edit within paragraph 15, regarding wait_until, as follows.

Effects:

Edit paragraph 17, regarding wait_until, as follows.

Returns: cv_status::timeout if the function unblocked because abs_time was reached the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time expired, otherwise cv_status::no_timeout.

Edit within paragraph 20, regarding wait_for, as follows.

Effects:

Edit paragraph 21, regarding wait_for, as follows.

Returns: cv_status::timeout if the function unblocked because rel_time elapsed the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time expired, otherwise cv_status::no_timeout.

Edit within paragraph 28, regarding the predicate wait_for, as follows.

Effects:

30.6.6 Class template future [futures.unique_future]

Edit within paragraph 22, regarding wait_for, as follows.

Effects: blocks until the associated asynchronous state is ready or until the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time has elapsed expired.

Edit within paragraph 23, regarding wait_for, as follows.

Returns:

Edit within paragraph 25, regarding wait_until, as follows.

Effects: blocks until the associated asynchronous state is ready or until the current time exceeds the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time has expired.

Edit within paragraph 26, regarding wait_until, as follows.

Returns:

30.6.7 Class template shared_future [futures.shared_future]

Edit within paragraph 27, regarding wait_for, as follows.

Effects: blocks until the associated asynchronous state is ready or until the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time has elapsed expired.

Edit within paragraph 28, regarding wait_for, as follows.

Returns:

Edit within paragraph 30, regarding wait_until, as follows.

Effects: blocks until the associated asynchronous state is ready or until the current time exceeds the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time has expired.

Edit within paragraph 31, regarding wait_until, as follows.

Returns:

30.6.8 Class template atomic_future [futures.atomic_future]

Edit within paragraph 23, regarding wait_for, as follows.

Effects: blocks until the associated asynchronous state is ready or until the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time has elapsed expired.

Edit within paragraph 24, regarding wait_for, as follows.

Returns:

Edit within paragraph 27, regarding wait_until, as follows.

Effects: blocks until the associated asynchronous state is ready or until the current time exceeds the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time has expired.

Edit within paragraph 28, regarding wait_until, as follows.

Returns: