ISO/IEC JTC1 SC22 WG21 N3191 = 10-0181 - 2010-11-12
Lawrence Crowl, crowl@google.com, Lawrence@Crowl.org
This paper revises ISO/IEC JTC1 SC22 WG21 N3128 = 10-0118 - 2010-08-19, addressing US 181 (LWG 1482). It also incorporates resolutions for US 191 (LWG 1495), CH 28 (LWG 1496), and CH 29 (LWG 1498).
In detail, this paper
steady_clock
(which are semantically implied by 'steady', but were not normative);
Problem
US 181 (LWG 1482)
US 191 (LWG 1495)
CH 28 (LWG 1496)
CH 29 (LWG 1498)
Solution
US 181 (LWG 1482)
US 191 (LWG 1495)
CH 28 (LWG 1496)
CH 29 (LWG 1498)
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.
In section 30.5,
the predicate wait
is defined
as a loop over the non-predicate wait
.
The predicate wait_until
is defined
as a loop over the non-predicate wait_until
.
The predicate wait_for
is not so implemented.
The problem is that
the return value from the non-predicate wait_for
operation
provides insufficient information
to synthesize the behavior of the predicate wait_for
.
This problem indicates that
the wait_for
facility is under-provisioned.
In section 30.5.1,
requiring wait_until
makes it impossible to implement condition_variable
correctly
using respective objects provided by the operating system
(i.e. implementing the native_handle()
function)
on many platforms (e.g. POSIX, Windows, MacOS X)
or using the same object as for the condition variable proposed for C.
In section 30.5,
it is unclear if a spurious wake-up during the loop
and reentering of the blocked state
due to a repeated execution of the loop
will adjust the timer of the blocking
with the respect to the previously specified rel_time
value.
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 we 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.]
Programmers can use the same technique as in
the resolution to CH 29 (below).
That is,
rewrite the wait_for
call
to use wait_until
on the steady_clock
instead.
The rewrite would then loop until
the steady_clock::now()
time
reached the desired value.
This technique is possible
because of the introduction of steady_clock
.
This paper relaxes the requirements on wait_until
operations
so that they are implementable with current systems.
Therefore, we preserve the interface as a normative requirement.
Rewrite the normative algorithm description
to a call on wait_until
on a steady_clock
.
See paragraphs 30.5.1/25, 30.5.1/34, 30.5.2/20, and 30.5.2/28.
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
ift1 <= t2
is alwaystrue
and 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
never decrease as physical time advances and 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. Implementations should 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 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 steady-clock relative timeout on the difference between Ct and the time point of the call to the
_until
function. [Note: Implementations should, but may not, decrease the duration of the wait when the clock is adjusted forwards. —end note]
Add another new paragraph as follows.
[Note: FIX If the clock is not synchronized with a steady clock, e.g. a CPU time clock, these timeouts may not provide useful functionality. —end note]
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 time specified by. Ifabs_time
abs_time
has already passed, the function attempts to obtain ownership without blocking (as if by callingtry_lock()
). The function shall return before thetimeabsolute timeout (30.2.4 [thread.req.timing]) 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 paragraph 25, regarding wait_for
, as follows.
Effects: as if
return wait_until(lock, chrono::steady_clock::now() + rel_time);
Atomically callslock.unlock()
and blocks on*this
.When unblocked, callslock.lock()
(possibly blocking on the lock), then returns.The function will unblock when signaled by a call tonotify_one()
or a call tonotify_all()
, by the elapsed timerel_time
passing (30.2.4), or spuriously.If the function exits via an exception,lock.lock()
shall be called prior to exiting the function scope.
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: as if
return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));
Executes a loop: Within the loop the function first evaluatespred()
and exits the loop if the result istrue
.Atomically callslock.unlock()
and blocks on*this
.When unblocked, callslock.lock()
(possibly blocking on the lock).The function will unblock when signaled by a call tonotify_one()
or a call tonotify_all()
, by the elapsed timerel_time
passing (30.2.4), or spuriously.If the function exits via an exception,lock.lock()
shall be called prior to exiting the function scope.The loop terminates whenpred()
returnstrue
or when the time duration 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 paragraph 20, regarding wait_for
, as follows.
Effects: as if
return wait_until(lock, chrono::steady_clock::now() + rel_time);
Atomically calls lock.unlock() and blocks on *this.When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.The function will unblock when signaled by a call to notify_one() or a call to notify_all(), by the elapsed time rel_time passing (30.2.4), or spuriously.If the function exits via an exception, lock.unlock() shall be called prior to exiting the function scope.
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 paragraph 28, regarding the predicate wait_for
,
as follows.
Effects: as if
return wait_until(lock, chrono::steady_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 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() or a call to notify_all(), by the elapsed time rel_time passing (30.2.4), 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.
This section has editorial conflicts with LWG 1518. We believe there are not substantive conflicts.
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.- ....
This section has editorial conflicts with LWG 1518. We believe there are not substantive conflicts.
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.- ....
This section has editorial conflicts with LWG 1518. We believe there are not substantive conflicts.
Section 30.6.8 [futures.atomic_future] may be removed by N3194. If so, the following edits are moot and may be ignored.
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.- ....