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]
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.
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.]
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.
Edit within the header synopsis as follows.
// Clocks class system_clock;
class monotonic_clock;class steady_clock; class high_resolution_clock;
Edit within table 56 as follows.
.... .... .... C1::is_monotonic
const bool
true
ift1 <= t2
is alwaystrue
, otherwisefalse
. [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, otherwisefalse
.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]
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); };
monotonic_clock
[time.clock.monotonic]steady_clock
[time.clock.steady]Delete paragraph 1.
Objects of classmonotonic_clock
represent clocks for which values oftime_point
never decrease as physical time advances.monotonic_clock
may be a synonym forsystem_clock
ifsystem_clock::is_monotonic
istrue
.
Edit paragraph 2 as follows.
The classObjects of classmonotonic_clock
is conditionally supported.steady_clock
represent clocks for which values oftime_point
advance at a steady rate relative to real time. That is, the clock may not be adjusted.class
monotonic_clocksteady_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(); };
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 forsystem_clock
ormonotonic_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(); };
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 arelative timeduration. These functions produce relative timeouts. Implementationsshouldshould use amonotonicsteady 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.
If Ca > Ct then the waiting function should wake as soon as possible, i.e. Ca+Di+Dm, since the timeout is already satisfied. [Note: This specification may result in the total duration of the wait decreasing when measured against a steady clock. —end note]
If Ca <= Ct then the waiting function should not time out until
Clock::now()
returns a time Cn >= Ct, i.e. waking at Ct+Di+Dm. [Note: When the clock is adjusted backwards, this specification may result in the total duration of the wait increasing when measured against a steady clock. When the clock is adjusted forwards, this specification may result in the total duration of the wait decreasing when measured against a steady clock. —end note]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.
Edit paragraph 6, regarding sleep_until
, as follows.
Effects: Blocks the calling thread
at least until the timefor the absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
.
Edit paragraph 9, regarding sleep_for
, as follows.
Effects: Blocks the calling thread
for at least the timefor the relative timeout (30.2.4 [thread.req.timing]) specified byrel_time
.
Edit paragraph 4, regarding try_lock_for
, as follows.
Effects: The function attempts to obtain ownership of the mutex within the
timerelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
. If the time specified byrel_time
is less than or equal to 0, the function attempts to obtain ownership without blocking (as if by callingtry_lock()
). The function shall return within thetimetimeout specified byrel_time
only if it has obtained ownership of the mutex object. [Note: As withtry_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
timeabsolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
. Ifabs_time
has already passed, the function attempts to obtain ownership without blocking (as if by callingtry_lock()
). The function shall return before thetimetimeout specified byabs_time
only if it has obtained ownership of the mutex object. [Note: As withtry_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 within paragraph 19, regarding wait_until
, as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,ifexpiration of the absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time <= Clock::now()
abs_time
, or spuriously.- ....
Edit paragraph 21, regarding wait_until
, as follows.
Returns:
cv_status::timeout
ifthe function unblocked becausethe absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
was reachedabs_time
expired, otherwisecv_status::no_timeout
.
Edit within paragraph 25, regarding wait_for
, as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,by the elapsed timeexpiration of the relative timeout (30.2.4 [thread.req.timing]) specified byrel_time passing
rel_time
, or spuriously.- ....
Edit paragraph 26, regarding wait_for
, as follows.
Returns:
cv_status::timeout
ifthe function unblocked becausethe relative timeout (30.2.4 [thread.req.timing]) specified byrel_time
elapsedrel_time
expired, otherwisecv_status::no_timeout
.
Edit within paragraph 34, regarding the predicate wait_for
,
as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,by the elapsed timeexpiration of the relative timeout (30.2.4 [thread.req.timing]) specified byrel_time passing
rel_time
, or spuriously.- ....
The loop terminates when
pred()
returns true or when thetime durationrelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
has elapsed.- ....
Edit within paragraph 15, regarding wait_until
, as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,ifexpiration of the absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time <= Clock::now()
abs_time
, or spuriously.- ....
Edit paragraph 17, regarding wait_until
, as follows.
Returns:
cv_status::timeout
ifthe function unblocked becausethe absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
was reachedabs_time
expired, otherwisecv_status::no_timeout
.
Edit within paragraph 20, regarding wait_for
, as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,by the elapsed timeexpiration of the relative timeout (30.2.4 [thread.req.timing]) specified byrel_time passing
rel_time
, or spuriously.- ....
Edit paragraph 21, regarding wait_for
, as follows.
Returns:
cv_status::timeout
ifthe function unblocked becausethe relative timeout (30.2.4 [thread.req.timing]) specified byrel_time
elapsedrel_time
expired, otherwisecv_status::no_timeout
.
Edit within paragraph 28, regarding the predicate wait_for
,
as follows.
Effects:
- ....
The function will unblock when signaled by a call to
notify_one()
or, a call tonotify_all()
,by the elapsed timeexpiration of the relative timeout (30.2.4 [thread.req.timing]) specified byrel_time passing
rel_time
, or spuriously.- ....
The loop terminates when
pred()
returns true or when thetime durationrelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
has elapsed.- ....
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
haselapsedexpired.
Edit within paragraph 23, regarding wait_for
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thetime periodrelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
haselapsedexpired.- ....
Edit within paragraph 25, regarding wait_until
, as follows.
Effects: blocks until the associated asynchronous state is ready or until
the current time exceedsthe absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
has expired.
Edit within paragraph 26, regarding wait_until
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thethe time pointabsolute timeout (30.2.4 [thread.req.timing]) specified byrel_time
hasbeen reachedexpired.- ....
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
haselapsedexpired.
Edit within paragraph 28, regarding wait_for
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thetime periodrelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
haselapsedexpired.- ....
Edit within paragraph 30, regarding wait_until
, as follows.
Effects: blocks until the associated asynchronous state is ready or until
the current time exceedsthe absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
has expired.
Edit within paragraph 31, regarding wait_until
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thethe time pointabsolute timeout (30.2.4 [thread.req.timing]) specified byrel_time
hasbeen reachedexpired.- ....
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
haselapsedexpired.
Edit within paragraph 24, regarding wait_for
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thetime periodrelative timeout (30.2.4 [thread.req.timing]) specified byrel_time
haselapsedexpired.- ....
Edit within paragraph 27, regarding wait_until
, as follows.
Effects: blocks until the associated asynchronous state is ready or until
the current time exceedsthe absolute timeout (30.2.4 [thread.req.timing]) specified byabs_time
has expired.
Edit within paragraph 28, regarding wait_until
, as follows.
Returns:
- ....
future_status::timeout
if the function is returning because thethe time pointabsolute timeout (30.2.4 [thread.req.timing]) specified byrel_time
hasbeen reachedexpired.- ....