common_type
ratio
duration
time_point
We all deal with time every day of our lives. We've intuitively known it since birth. Thus we are all very familiar with it and believe it to be a simple matter. The modeling of time in computer programs should be similarly simple.
The unfortunate truth is that this perceived simplicity is only skin deep. Fortunately however, we do not need a terribly complicated solution to meet the bulk of our needs. However, overly simplistic solutions are fraught with danger, inherent inefficiency, and lack the ability to adapt as the computer industry evolves over decades.
The C library has had basic time facilities for several decades. It contains both the notion of a point in time, and the time duration between two points in time. It also has a clock to get the current time (point).
// A type capable of representing a point in time. time_t // A function returning a time duration between two points in time. double difftime(time_t t1, time_t t2); // A function returning the current point in time. time_t time(time_t*);
The C library also contains functions relating points in time to the Gregorian calendar, and for performing I/O with points in time.
For a time point to have meaning, one must be able relate or compare two time points. This generally requires an epoch which is simply a special point in time against which all other points in time relate themselves to. Without an epoch (or common reference) it becomes impossible to compare two points in time. However to compare two points in time, all one needs to know is that they share the same epoch. Knowledge of when that epoch is, isn't needed.
The C committee decided to not specify the epoch to which
time_t
is measured against, nor the precision it is
measured with. The advantage of this
is that platforms can more easily change the definition of the epoch
since no code can portably depend on exactly when it was. If a process
needs to import or export a point in time with another process, it must
first relate the point in time to an agreed upon epoch, such as that
used by the Gregorian calendar.
POSIX specified time_t
to represent seconds, and over the
decades it has become common for time_t
to be a signed 32
bit integral type which is simply a count of seconds since New Years
1970. De-facto standardizing on this definition has created some serious
problems:
difftime
can not return a time duration with more precision
than a second.
One solution to the time_t
problem is to simply extend it to
a signed 64 bit integer. This would get rid the of the range problem
forever (for all practical purposes). However, the precision problem remains.
POSIX first published a solution to the precision problem with a new clock to get "now", and a new representation:
struct timeb { time_t time; // seconds unsigned short millitm; // milliseconds }; int ftime(struct timeb *tp);
This function is currently marked LEGACY in the POSIX documentation.
Next came a new clock, and a new structure with microsecond precision:
struct timeval { time_t tv_sec; // seconds int_least20_t tv_usec; // microseconds }; int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
This function is still in wide use today on many platforms. However, it is not the end of the line. POSIX later released a new clock, and a new structure. This new function actually supports multiple clocks and has nanosecond precision.
struct timespec { time_t tv_sec; // seconds long tv_nsec; // nanoseconds }; int clock_gettime(clockid_t, struct timespec *);
In summary, there has been a progression of C interfaces (outside of the C standard) with precisions of second, millisecond, microsecond and nanosecond. Each requires a new interface, both for the structural representation, and for its associated clock to find the current point in time.
Is nanosecond precision the end of the line? Every computer Apple currently ships, has a monotonic clock which reports the current time point in units of a single nanosecond. Most of these machines have a cpu clock speed at or above 2GHz. The amount of time between two ticks of a 2GHz clock is only one half of a nanosecond (500 picoseconds).
The future is always hard to predict. However, over the next 10 to 20 years, should the need arise to traffic in time durations finer than nanosecond, we would not be surprised. We are already bumping up against 1 nanosecond precision.
Should the C++ committee standardize a time point / time duration representation with nanosecond precision now? And be prepared to standardize yet another representation with finer precision 10 (or even 5) years from now? In the long run, is such a plan really simple? Or does it just seem so when we only have to worry about nanoseconds today?
This paper proposes a solution that is precision neutral.
C++ offers unique opportunities in the representation of time. Other languages have had to address this problem as well. But none of them had the tools which C++ has which can be put to use in the solution of this problem. The C++0x standard library's multi-threading library requires the ability to deal with the representation of time in a manner consistent with modern C++ practices. This paper proposes a solution to both the standard library need and C++ user needs with a very simple end user interface which supports multiple clocks, multiple precisions (both coarser and finer than we will ever need), separate types for points in time and time durations, efficiency, and compile time enforced safety.
In addition to the clocks provided by the proposal, users can easily create their own clocks, with both points in time and time durations which have a representation and precision of their own choosing. For example if there is a hardware counter which simply increments a count with each cycle of the cpu, one can very easily build clocks, time points and durations on top of that, using only a few tens of lines of code. Such systems can be used to call the time-sensitive threading API's such as sleep, wait on a condition variable, or wait for a mutex lock. The API proposed herein is not sensitive as to whether this is a 300MHz clock (with a 31⁄3 nanosecond tick period) or a 3GHz clock (with a tick period of 1⁄3 of a nanosecond). And the resulting code will be just as efficient as if the user wrote a special purpose clock cycle counter.
duration
.
Examples of time durations include seconds
, minutes
and
nanoseconds
. All of these units of time duration are united with a generic interface
by the duration
facility.
time_point
.
A time_point
represents an epoch plus or minus a duration. This paper leaves
epochs unspecified. A time_point
is associated with a clock.
system_clock
, monotonic_clock
and high_resolution_clock
.
A clock
is a symbolic bundle of a native time_point
and
duration
, and a function which returns a time_point
representing now.
clock
s, time_point
s and duration
s proposed herein.
Additionally, a minimal amount of general purpose infrastructure is proposed which will
support both the interface and implementation of the clock
s,
time_point
s and duration
s proposed herein. It is expected that
these general purpose facilities will also find utility in far ranging user applications
as well.
common_type
is a facility which is useful in specifying the type of the result of
functions and operators which take a variety of types (e.g. "mixed mode" complex arithmetic).
ratio
is a facility which is useful in specifying compile time rational
constants. Compile time rational arithmetic is supported with protection against overflow
and divide by zero. Such a facility is very handy when needing to efficiently represent
1⁄3 of a
nanosecond, or specifying an inch in terms of meters
(for example 254⁄10000 meters).
This paper does not offer calendrical services except for a minimal mapping to
and from C's time_t
.
As this paper does not propose a date/time library, nor specify epochs, it also does not address leap seconds. However, a date/time library will find this to be an excellent foundation on which to build.
This paper does not propose a general purpose physical quantities library.
This paper proposes a solid foundation that, in the future, could provide a compatible starting point for a general physical units library. While such a future library might take any of several forms, the present proposal stops well short of actually being a physical units library. This proposal is time-specific, and continues to be motivated by the time-related needs of the threading library.
The major goal of this proposal is to satisfy the needs of the standard library threading API in a manner which is easy to use, safe to use, efficient, and flexible enough to not be obsolete 10 or even 100 years from now. Every feature contained in this proposal is here for a specific reason with practical use cases as motivation. Things that fell into the category of "cool", or "that sounds like it might be useful", or "very useful but not needed by this interface" have not been included. Such items might appear in other proposals, and possibly target a TR.
The proposal is entirely an add-on. It will break no existing C++03 code,
subject to the usual caveat that user code giving a using namespace
std
may see name clashes. Even that possibility is mitigated because
the time portion of the proposal is in a std sub-namespace.
common_type
common_type
has been a recurring theme in many places for many years.
We've previously known it as promote
and examples of it are spread
throughout boost. It has been reinvented independently
several times, because it is so useful.
Andrei Alexandrescu recently pointed us at a D library: std.traits - D Programming Language - Digital Mars, which became the motivation for this particular name, and the variadic nature of this trait.
In a nutshell, common_type
is a trait that takes 1 or more types, and
returns a type which all of the types will convert to. The default definition demands
this conversion be implicit. However the trait can be specialized for user-defined
types which want to limit their inter-type conversions to explicit, and yet still
want to interoperate with the common_type
facility.
Example:
template <class T, class U> typename common_type<complex<T>, complex<U>>::type operator+(complex<T>, complex<U>);
In the above example, "mixed-mode" complex arithmetic is allowed. The return
type is described by common_type
. For example the resulting type
of adding a complex<int>
and complex<double>
might be a
complex<double>
. Another choice for the author might be:
template <class T, class U> complex<typename common_type<T, U>::type> operator+(complex<T>, complex<U>);
Here is how someone might produce a variadic comparison function:
template <class ...T> typename common_type<T...>::type min(T... t);
This is a very useful and broadly applicable utility. The duration
and time_point
facilities use it to make multi-precision arithmetic
seamless and exact.
The cost of not including common_type
is that it is very likely that
the implementation would include it anyway, but spell it __common_type
instead. This
would prevent authors of arithmetic emulators from using their classes as representations
with duration
s unless the emulator had exactly one implicit conversion to or from an
arithmetic type. This would be a large loss of functionality from the user's point
of view, possibly mandating a less safe interface for the user's arithmetic emulator.
common_type
Proposed WordingInsert into 20.4.2 [meta.type.synop]
template <class ...T> struct common_type;
Modify 20.4.2 [meta.type.synop] paragraph 1:
The behavior of a program that adds specializations for any of the class templates defined in this subclause is undefined unless otherwise specified.
Add a row to 20.4.7 [meta.trans.other]:
template <class ...T> struct common_type; template <class T> struct common_type<T> { typedef T type; }; template <class T, class U> struct common_type<T, U> { private: static T&& t(); static U&& u(); public: typedef decltype(true ? t() : u()) type; }; template <class T, class U, class ...V> struct common_type<T, U, V...> { typedef typename common_type<typename common_type<T, U>::type, V...>::type type; };All types in the parameter pack
T
shall be complete. This trait is permitted to be specialized by a user if at least one template parameter is a user-defined type. [Note: Such specializations are required when only explicit conversions are desired among thecommon_type
arguments. -- end note]
The single parameter common_type
can now replace identity
and
avoid the historical conflict associated with identity
(reference LWG issues
700 and
823).
Modify 20.2 [utility]:
// 20.2.2, forward/move:template <class T> struct identity;template <class T> T&& forward(typenameidentitycommon_type<T>::type&&); template <class T> typename remove_reference<T>::type&& move(T&&);
Modify 20.2.2 [forward]:
template <class T> struct identity { typedef T type; const T& operator()(const T& x) const; };const T& operator()(const T& x) const;-2- Returns:x
template <class T> T&& forward(typenameidentitycommon_type<T>::type&& t);[Note: The use offorces users to explicitly specify the template parameter. This is necessary to get the correct forwarding semantics. -- end note]
identitycommon_type
ratio
ratio
is a general purpose utility inspired by Walter Brown allowing one to easily and
safely compute rational values at compile time. The ratio class
catches all errors (such as divide by zero and overflow) at compile
time. It is used in the duration
and
time_point
libraries to efficiently create units of time.
It can also be used in other "quantity" libraries (both std-defined and
user-defined), or anywhere there is a rational constant which is known at
compile time. The use of this utility can greatly reduce the chances of
run time overflow because the ratio
(and any
ratio
s resulting from ratio
arithmetic) are
always reduced to lowest terms.
ratio
is a template taking two intmax_t
s, with the
second defaulted to 1. It only has two public members, both of which are
static const intmax_t
. One is the numerator of the ratio and the
other is the denominator. The ratio
is always normalized such that
it is expressed in lowest terms, and the denominator is always positive. When
the numerator is 0, the denominator is always 1.
Example:
typedef ratio<5, 3> five_thirds; // five_thirds::num == 5, five_thirds::den == 3 typedef ratio<25, 15> also_five_thirds; // also_five_thirds::num == 5, also_five_thirds::den == 3 typedef ratio_divide<five_thirds, also_five_thirds>::type one; // one::num == 1, one::den == 1
This facility also includes convenience typedefs for the SI prefixes atto through exa corresponding to
their internationally recognized definitions (in terms of ratio
). This is a tremendous
syntactic convenience. It will prevent errors in specifying constants as one no longer has to
double count the number of zeros when trying to write million or billion.
Example:
typedef ratio_multiply<ratio<5>, giga>::type _5giga; // _5giga::num == 5000000000, _5giga::den == 1 typedef ratio_multiply<ratio<5>, nano>::type _5nano; // _5nano::num == 1, _5nano::den == 200000000
The cost of not including ratio
would mean that the implementor would likely have this
functionality anyway, but spell it __ratio
instead. This would prevent the user from
using ratio
in his own code. Furthermore duration
would have to be templated on two long long
s instead of on ratio
like so:
template <class Rep, long long N, long long D> duration
This would mean that clients wanting to build a custom duration type (say a nanosecond
represented by a double
) would have to write:
duration<double, 1, 1000000000LL>
instead of:
duration<double, nano>
This lack of syntatic niceness, along with the loss of functionality in
the reuse of ratio
in user-written code seems to indicate
that the loss of ratio
would be a sizeable loss to user
code.
ratio
Proposed WordingInsert a new section in 20 [utilities].
Compile time rational arithmetic [ratio]
This subclause describes a class template
ratio
which exactly represents any finite rational number with a numerator and denominator representable byintmax_t
. The numerator and denominator shall be compile time integral constants.Header
<ratio>
synopsis [ratio.synop]namespace std { template <intmax_t N, intmax_t D = 1> class ratio; // ratio arithmetic template <class R1, class R2> struct ratio_add; template <class R1, class R2> struct ratio_subtract; template <class R1, class R2> struct ratio_multiply; template <class R1, class R2> struct ratio_divide; // ratio comparison template <class R1, class R2> struct ratio_equal; template <class R1, class R2> struct ratio_not_equal; template <class R1, class R2> struct ratio_less; template <class R1, class R2> struct ratio_less_equal; template <class R1, class R2> struct ratio_greater; template <class R1, class R2> struct ratio_greater_equal; // convenience SI typedefs typedef ratio<1, 1000000000000000000000000> yocto; // conditionally supported typedef ratio<1, 1000000000000000000000> zepto; // conditionally supported typedef ratio<1, 1000000000000000000> atto; typedef ratio<1, 1000000000000000> femto; typedef ratio<1, 1000000000000> pico; typedef ratio<1, 1000000000> nano; typedef ratio<1, 1000000> micro; typedef ratio<1, 1000> milli; typedef ratio<1, 100> centi; typedef ratio<1, 10> deci; typedef ratio< 10, 1> deca; typedef ratio< 100, 1> hecto; typedef ratio< 1000, 1> kilo; typedef ratio< 1000000, 1> mega; typedef ratio< 1000000000, 1> giga; typedef ratio< 1000000000000, 1> tera; typedef ratio< 1000000000000000, 1> peta; typedef ratio< 1000000000000000000, 1> exa; typedef ratio< 1000000000000000000000, 1> zetta; // conditionally supported typedef ratio<1000000000000000000000000, 1> yotta; // conditionally supported } // namespace std
ratio
[ratio.ratio]namespace std { template <intmax_t N, intmax_t D = 1> class ratio { public: static const intmax_t num; static const intmax_t den; }; } // namespace stdA diagnostic shall be emitted if
ratio
is instantiated withD == 0
, or if the absolute value ofN
orD
can not be represented. [Note: These rules ensure that infinite ratios are avoided and that for any negative input, there exists a representable value of its absolute value which is positive. In a two's complement representation, this excludes the most negative value. -- end note]Let
gcd
denote the greatest common divisor ofN
's absolute value and ofD
's absolute value.
num
shall have the valuesign(N)*sign(D)*abs(N)/gcd
.
den
shall have the valueabs(D)/gcd
.
ratio
arithmetic [ratio.arithmetic]For each of the class templates in this clause, each template parameter shall refer to a
ratio
. If the implementation is unable to form the indicatedratio
due to overflow, a diagnostic shall be issued.template <class R1, class R2> struct ratio_add {typedef see below type;};type
shall aliasratio<R1::num * R2::den + R2::num * R1::den, R1::den * R2::den>
.template <class R1, class R2> struct ratio_subtract {typedef see below type;};type
shall aliasratio<R1::num * R2::den - R2::num * R1::den, R1::den * R2::den>
.template <class R1, class R2> struct ratio_multiply {typedef see below type;};type
shall aliasratio<R1::num * R2::num, R1::den * R2::den>
.template <class R1, class R2> struct ratio_divide {typedef see below type;};type
shall aliasratio<R1::num * R2::den, R2::num * R1::den>
.
ratio
comparison [ratio.comparison]template <class R1, class R2> struct ratio_equal : public integral_constant<bool, see below> {};IfR1::num == R2::num && R1::den == R2::den
,ratio_equal
derives fromtrue_type
, else derives fromfalse_type
.template <class R1, class R2> struct ratio_less : public integral_constant<bool, see below> {};IfR1::num * R2::den < R2::num * R1::den
,ratio_less
derives fromtrue_type
, else derives fromfalse_type
. Implementations are permitted to use more complex algorithms to compute the above relationship to avoid overflow. If the implementation is not able to avoid overflow, a diagnostic shall be emitted.template <class R1, class R2> struct ratio_not_equal : public integral_constant<bool, !ratio_equal<R1, R2>::value> {}; template <class R1, class R2> struct ratio_less_equal : public integral_constant<bool, !ratio_less<R2, R1>::value> {}; template <class R1, class R2> struct ratio_greater : public integral_constant<bool, ratio_less<R2, R1>::value> {}; template <class R1, class R2> struct ratio_greater_equal : public integral_constant<bool, !ratio_less<R1, R2>::value> {};
ratio
SI typedefs [ratio.si]Four of the typedefs in the synopsis are conditionally supported:yocto
,zepto
,zetta
andyotta
. If the constants specified in the synopsis are representable byintmax_t
, the typedef shall be supported as shown in the synopsis, otherwise it shall not.
duration
The duration
is the heart of this proposal. The interface that the user will
see in everyday use is nearly identical to that of boost time durations authored by Jeff Garland,
both in syntax and in behavior. This has been a very popular boost library for 7 years. There
is an enormous positive history with this interface.
The library consists of six units of time duration:
hours
minutes
seconds
milliseconds
microseconds
nanoseconds
These units were chosen as a subset of the boost library because they are the most common units used when
sleeping, waiting on a condition variable, or waiting to obtain the lock on a mutex. Each of these units
is nothing but a thin wrapper around a signed integral count. That is, when you construct minutes(3)
,
all that happens is a 3
is stored inside of minutes
. When you construct
microseconds(3)
, all that happens is a 3
is stored inside of microseconds
.
The only context in which these different types differ is when being
converted to one another. At this time, unit-specific compile-time
conversion constants are used to convert the source unit to the target
unit. Only conversions from coarser units to finer units are allowed
(in boost). This restriction ensures that all conversions are always
exact. That is, microseconds
can always represent any
value minutes
has.
In the boost library, these units are united via inheritance. This paper instead unites these
units through the class template duration
. That is, in this proposal all six of
the above units are nothing but typedef
s to different instantiations of duration
.
This change from the boost library has a far reaching positive impact, while not changing the syntax of
the everyday use at all.
The most immediate positive impact is that the library can immediately generate any unit, any precision it
needs. This is sometimes necessary when doing comparisons or arithmetic between duration
s of
differing precision, assuming one wants the comparison and arithmetic to be exactly correct.
A secondary benefit is that by publishing the class template duration
interface, user
code can very easily create durations
with any precision they desire. The ratio
utility is used to specify the precision, so as long as the precision can be expressed by a rational constant
with respect to seconds, this framework can exactly represent it (one third of a second is no problem,
and neither is one third of a femto second). All of this utility and flexibility comes at no cost just by
making use of the no-run-time-overhead ratio
facility.
In the boost library, hours
does not have the same representation as nanoseconds
.
The former is usually represented with a long
whereas a long long
is required
for the latter. The reason for this is simply range. You don't need many hours
to cover
an extremely large range of time. But this isn't true of nanoseconds
. Being able to
reduce the sizeof
overhead for some units when possible, can be a significant performance
advantage.
This proposal continues, and generalizes that philosophy. Not only can one specify the precision of
a duration
, one can also specify its representation. This can be any integral type,
or even a floating point type. Or it can be a user-defined type which emulates an arithmetic type.
The six predefined units all use signed integral types as their representation. And they all have a
minimum range of +/- 292 years. nanoseconds
needs 64 bits to cover that range. hours
needs only 23 bits to cover that range.
duration
and How Do I Use One?
A duration
has a representation and a tick period (precision).
template <class Rep, class Period = ratio<1>> class duration;
The representation is
simply any arithmetic type, or an emulation of such a type. The representation stores a
count of ticks. This count is the only data member stored in a duration
. If the
representation is floating point, it can store fractions of a tick to the precision of the
representation. The tick period is represented by a ratio
and is encoded into
the duration
's type, instead of stored. The tick period only has an impact on
the behavior of the duration
when a conversion between different duration
's
is attempted. The tick period is completely ignored when simply doing arithmetic among like
duration
s.
Example:
typedef duration<long, ratio<60>> minutes; minutes m1(3); // m1 stores 3 minutes m2(2); // m2 stores 2 minutes m3 = m1 + m2; // m3 stores 5 typedef duration<long long, micro> microseconds; microseconds us1(3); // us1 stores 3 microseconds us2(2); // us2 stores 2 microseconds us3 = us1 + us2; // us3 stores 5 microseconds us4 = m3 + us3; // us4 stores 300000005
In the final line of code above, there is an implicit conversion from minutes to microseconds, resulting in a relatively large number of microseconds.
If you need to access the tick count within a duration
, there is a member count()
which simply returns the stored tick count.
long long tc = us4.count(); // tc is 300000005
These duration
s have very simple, very predictable, and very observable behavior.
After all, this is really nothing but the time tested interface of Jeff's boost time duration library
(unified with templates instead of inheritance).
m3 + us3
to minutes
instead of microseconds
?
minutes m4 = m3 + us3;
Answer: It won't compile. The rationale is that implicit truncation error should not be allowed to
happen. If this were to compile, then m4
would hold 5, the same value as m3
.
The value associated with us3
has been effectively ignored. This is similar to the problem
of assigning a double
to an int
: the fractional part gets silently discarded.
Answer: There is a duration_cast
facility to explicitly ask for this behavior:
minutes m4 = duration_cast<minutes>(m3 + us3); // m4.count() == 5
In general, one can perform duration
arithmetic at will. If duration_cast
isn't used, and it compiles, the arithmetic is exact. Any place one wants to override this
exact arithmetic behavior, duration_cast
can be used to explicitly specify that desire.
The duration_cast
has the same efficiency as the implicit conversion, and will even be
exact as often as it can.
duration
s. I don't want to deal with writing
duration_cast
all over the place. I'm content with the precision of my floating point
representation.
Answer: Not a problem. When the destination of a conversion has floating point representation, all conversions are allowed to happen implicitly.
How expensive is all of this?typedef duration<double, ratio<60>> dminutes; dminutes dm4 = m3 + us3; // dm4.count() == 5.000000083333333
Answer: If you were writing these conversions by hand, you could
not make it more efficient. The use of ratio
ensures that
all conversion constants are simplified as much as possible at compile
time. This usually results in the numerator or denominator of the
conversion factor simplifying to 1, and being subsequently ignored in
converting the run time values of the tick counts.
Answer: Yes. There is a complete example implementation of this proposal, including test suite.
The duration
part of it has a count of 99 semicolons. This count includes
the duration_cast
facility which handles all conversions, the duration
class template, with all arithmetic and comparison operators, and the six pre-defined units:
hours
, minutes
, seconds
, milliseconds
,
microseconds
and nanoseconds
.
Answer: There are several options open to the user:
If the author of the function wants to accept any duration, and is willing to work in floating point durations, he can simply use any floating point duration as the parameter:
void f(duration<double> d) // accept floating point seconds { // d.count() == 3.e-6 when passed microseconds(3) } f(microseconds(3));
If the author of the function wants to traffic only in integral durations, and
is content with handling nothing finer than say nanoseconds
(just
as an example), he can simply specify nanoseconds
as the parameter:
void f(nanoseconds d) { // d.count() == 3000 when passed microseconds(3) } f(microseconds(3));
In this design, if the client wants to pass in a floating point duration, or
a duration of finer precision than nanoseconds
, then the client is responsible
for choosing his own rounding mode in the conversion to nanoseconds
.
duration<double> s(1./3); // 1/3 of a second f(duration_cast<nanoseconds>(s)); // round towards zero in conversion to nanoseconds
In the example above, the client of f
has chosen "round towards zero" as the
desired rounding mode to nanoseconds
. If the client has a duration
that won't exactly convert to nanoseconds
, and fails to choose how the conversion
will take place, the compiler will refuse the call:
f(s); // does not compile
If the author of the function wants to accept any duration, but wants to work with integral representations and wants to control the rounding mode internally, then he can template the function:
template <class Rep, class Period> void f(duration<Rep, Period> d) { // convert d to nanoseconds, rounding up if it is not an exact conversion nanoseconds ns = duration_cast<nanoseconds>(d); if (ns < d) ++ns; // ns.count() == 333333334 when passed 1/3 of a floating point second } f(duration<double>(1./3));
If the author in the example does not want to accept floating point based durations, he can enforce that behavior like so:
template <class Period> void f(duration<long long, Period> d) { // convert d to nanoseconds, rounding up if it is not an exact conversion nanoseconds ns = duration_cast<nanoseconds>(d); if (ns < d) ++ns; // ns.count() == 333333334 when passed 333333333333 picoseconds } f(duration<long long, pico>(333333333333)); // About 1/3 of a second worth of picoseconds
Clients with floating point durations who want to use f
will now have to convert to
an integral duration themselves before passing the result to f
.
In summary, the author of f
has quite a bit of flexibility and control in the interface
he wants to provide his clients with, and easy options for manipulating that duration
internal to his function.
Answer: No. No matter which option the author of f
chooses above, the
following client code will not compile:
f(3); // Will not compile, 3 is not implicitly convertible to any duration
While duration
s only have precision and representation to concern themselves,
clocks and time_point
s are intimately related and refer to one another. Because
clocks are simpler to explain, we will do so first without fully explaining time_point
s.
Once clocks are introduced, it will be easier to then fill in what a time_point
is.
A clock is a concept which bundles 3 things:
duration
type.time_point
type.now()
which returns the concrete time_point
.This paper proposes 3 concrete clocks:
system_clock
monotonic_clock
high_precision_clock
A given platform may not be able to supply all three of these clocks. The user is also able to easily create more clocks.
Given a clock named Clock
, it will have:
class Clock { public: typedef an arithmetic-like type rep; typedef an instantiation ofratio
period; typedef std::chrono::duration<rep, period> duration; typedef std::chrono::time_point<Clock> time_point; static const bool is_monotonic = true or false; static time_point now(); };
One can get the current time from Clock
with:
Clock::time_point t1 = Clock::now();
And one can get the time duration between two time_point
s associated with Clock
with:
Clock::duration d = t1 - Clock::now();
And one can specify a past or future time_point
with:
Clock::time_point t2 = Clock::now() + d;
Note how even if a particular clock becomes obsolete, the next clock in line will have the same
API. There is no new learning curve to come up. The only source code changes will be simply
changing the type of the clock. The same duration
and time_point
framework continues to work as new clocks are introduced. And multiple clocks are safely
and easily handled within the same program.
time_point
A time_point
represents a point in time, as opposed to a duration of time.
Another way of saying the same thing, is that a time_point
represents an
epoch plus or minus a duration
. Examples of time_point
s include:
In each of the examples above, a different epoch is implied. Sometimes an epoch has meaning
for several millennia. Other times the meaning of an epoch is lost after a while (such as the
start of a timer, or when the computer booted). However,
if two time_point
s are known to share the same epoch, they can be subtracted,
yielding a valid duration
, even if the definition of the epoch no longer
has meaning.
In this proposal, an epoch is a purely abstract and unspecified concept. There is no
type representing an epoch. It is simply an idea that relates (or doesn't) time_point
s
to a clock, and in the case that they share a clock, time_point
s to one another.
time_point
s associated with different clocks are generally not interoperable unless
the relationship between the epochs associated with each clock is known.
time_point
and How Do I Use One?
A time_point
has a clock and a duration
.
template <class Clock, class Duration = typename Clock::duration> class time_point;
The time_point
's clock is not stored. It is simply embedded into the
time_point
's type and serves two purposes:
time_point
s originating from different clocks have different types,
the compiler can be instructed to fail if incompatible time_point
s are used
in inappropriate ways.time_point
, one often needs to compare that time_point
to "now". This is very simple as long as the time_point
knows what clock
it is defined with respect to.
A time_point
's duration
is stored as the only data member of
the time_point
. Thus time_point
s and their corresponding duration
have exactly the same layout. But they have very different meanings. For example, it is one thing
to say I want to sleep for 3 minutes. It is a completely different thing to say I want to sleep until
3 minutes past the time I started that timer (unless you just happened to start that timer now).
Both meanings (and options for sleeping) have great practical value in common use cases for
sleeping, waiting on a condition variable, and waiting for a mutex's lock. These same concepts and
tools are found (for example) in Ada.
A timer example:
void f() { monotonic_clock::time_point start = monotonic_clock::now(); g(); h(); duration<double> sec = monotonic_clock::now() - start; cout << "f() took " << sec.count() << " seconds\n"; }
Note that if one is using the duration
between two clock time_point
s in a way
where the precision of the duration
matters, it is good practice to convert the clock's
native duration
to a known duration. This insulates the code from future changes which
may be made to the clock's native precision in the future.
For example monotonic_clock
could easily be based on the clock speed of the cpu.
When you upgrade to a faster machine, you do not want your code that assumed a certain tick period
of this clock to start experiencing run time failures because your timing code has silently changed
meaning.
A delay loop example:
// delay for at least 500 nanoseconds: auto go = monotonic_clock::now() + nanoseconds(500); while (monotonic_clock::now() < go) ;
The above code will delay as close as possible to half a microsecond, no matter what the precision
of monotonic_clock
is. The more precise monotonic_clock
becomes, the
more accurate will be the delay to 500 nanoseconds.
Add a new subsection to 20.7 [date.time]:
Time utilities [time]
This subclause contains generally useful time utilities, also used in section 30 [thread].
Header <chrono> synopsis [chrono.synop]
namespace std { namespace chrono { template <class Rep, class Period = ratio<1>> class duration; template <class Clock, class Duration = typename Clock::duration> class time_point; } // namespace chrono // common_type traits template <class Rep1, class Period1, class Rep2, class Period2> struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>>; template <class Clock, class Duration1, class Duration2> struct common_type<chrono::time_point<Clock, Duration1>, chrono::time_point<Clock, Duration2>>; namespace chrono { // customization traits template <class Rep> struct treat_as_floating_point; template <class Rep> struct duration_values; // duration arithmetic template <class Rep1, class Period1, class Rep2, class Period2> typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator*(const duration<Rep1, Period>& d, const Rep2& s); template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator*(const Rep1& s, const duration<Rep2, Period>& d); template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator/(const duration<Rep1, Period>& d, const Rep2& s); template <class Rep1, class Period1, class Rep2, class Period2> typename common_type<Rep1, Rep2>::type operator/(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); // duration comparisons template <class Rep1, class Period1, class Rep2, class Period2> bool operator==(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> bool operator!=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> bool operator< (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> bool operator<=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> bool operator> (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Rep2, class Period2> bool operator>=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs); // duration_cast template <class ToDuration, class Rep, class Period> ToDuration duration_cast(const duration<Rep, Period>& d); // convenience typedefs typedef duration<signed integral type of at least 64 bits, nano> nanoseconds; typedef duration<signed integral type of at least 55 bits, micro> microseconds; typedef duration<signed integral type of at least 45 bits, milli> milliseconds; typedef duration<signed integral type of at least 35 bits > seconds; typedef duration<signed integral type of at least 29 bits, ratio< 60>> minutes; typedef duration<signed integral type of at least 23 bits, ratio<3600>> hours; // time_point arithmetic template <class Clock, class Duration1, class Rep2, class Period2> time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type> operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs); template <class Rep1, class Period1, class Clock, class Duration2> time_point<Clock, typename common_type<duration<Rep1, Period1>, Duration2>::type> operator+(const duration<Rep1, Period1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Rep2, class Period2> time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type> operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs); template <class Clock, class Duration1, class Duration2> typename common_type<Duration1, Duration2>::type operator-(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); // time_point comparisons template <class Clock, class Duration1, class Duration2> bool operator==(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Duration2> bool operator!=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Duration2> bool operator< (const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Duration2> bool operator<=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Duration2> bool operator> (const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); template <class Clock, class Duration1, class Duration2> bool operator>=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs); // time_point_cast template <class ToDuration, class Clock, class Duration> time_point<Clock, ToDuration> time_point_cast(const time_point<Clock, Duration>& t); // Clocks class system_clock; class monotonic_clock; class high_resolution_clock; } // namespace chrono } // namespace stdClock Requirements [chrono.clock.req]
A clock represents a bundle consisting of a native
duration
, a nativetime_point
, and a functionnow()
to get the currenttime_point
. A clock shall meet the requirements in Table ?.In Table ?
C1
andC2
denote clock types.t1
andt2
are values returned fromC1::now()
where the call returningt1
happens before [intro.multithread] the call returningt2
and both of these calls happen beforeC1::time_point::max()
.
Table ?: Clock Requirements expression return type operational semantics C1::rep
An arithmetic type or class emulating an arithmetic type. The representation type of the native duration
andtime_point
.C1::period
ratio
The tick period of the clock in seconds. C1::duration
chrono::duration<C1::rep, C1::period>
The native duration
type of the clock.C1::time_point
chrono::time_point<C1>
orchrono::time_point<C2, C1::duration>
The native time_point
type of the clock. Different clocks are permitted to share atime_point
definition if it is valid to compare theirtime_point
s by comparing their respectiveduration
s.C1
andC2
shall refer to the same epoch.C1::is_monotonic
const bool
true
ift1 <= t2
is alwaystrue
, elsefalse
. [Note: A clock that can be adjusted backwards is not monotonic. -- end note]C1::now()
C1::time_point
Returns a time_point
representing the current point in time.Time related traits [chrono.traits]
template <class Rep> struct treat_as_floating_point : is_floating_point<Rep> {};The
duration
template uses thetreat_as_floating_point
trait to help determine if aduration
with one tickperiod
can be converted to anotherduration
with a different tickperiod
. Iftreat_as_floating_point<Rep>::value
istrue
, thenRep
is a floating point type and implicit conversions are allowed amongduration
s. Otherwise, the implicit convertibility depends on the tickperiod
s of theduration
s. IfRep
is a class type which emulates a floating point type, the author ofRep
can specializetreat_as_floating_point
so thatduration
will treat thisRep
as if it were a floating point type. OtherwiseRep
is assumed to be an integral type, or a class emulating an integral type.template <class Rep> struct duration_values { public: static constexpr Rep zero(); static constexpr Rep max(); static constexpr Rep min(); };The
duration
template uses theduration_values
trait to construct special values of theduration
s representation (Rep
). This is done because the representation might be a class type with behavior which requires some other implementation to return these special values. In that case, the author of that class type should specializeduration_values
to return the indicated values.static constexpr Rep zero();Returns:
Rep(0)
. [Note:Rep(0)
is specified instead ofRep()
sinceRep()
may have some other meaning, such as an uninitialized value. -- end note]Remarks: The value returned shall correspond to the additive identity.
static constexpr Rep max();Returns:
numeric_limits<Rep>::max()
.Remarks: The value returned shall compare greater than
zero()
.static constexpr Rep min();Returns:
numeric_limits<Rep>::lowest()
.Remarks: The value returned shall compare less than or equal to
zero()
.
common_type
specializationstemplate <class Rep1, class Period1, class Rep2, class Period2> struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>> { typedef chrono::duration<typename common_type<Rep1, Rep2>::type, see below> type; };The
period
of theduration
indicated by this specialization ofcommon_type
shall be the greatest common divisor ofPeriod1
andPeriod2
. This can be computed by forming aratio
of the greatest common divisor ofPeriod1::num
andPeriod2::num
, and the least common multiple ofPeriod1::den
andPeriod2::den
.Note: The
typedef type
is theduration
with the largest tickperiod
possible where bothduration
arguments will convert to it without requiring a division operation. The representation of this type is intended to be able to hold any value resulting from this conversion, with the possible exception of round-off error when floating pointduration
s are involved (but not truncation error).template <class Clock, class Duration1, class Duration2> struct common_type<chrono::time_point<Clock, Duration1>, chrono::time_point<Clock, Duration2>> { typedef chrono::time_point<Clock, typename common_type<Duration1, Duration2>::type> type; };The
common_type
of twotime_point
s is atime_point
with the same clock (both shall have the same clock), and thecommon_type
of the twoduration
s.Class template
duration
[chrono.duration]A
duration
measures time between two points in time (time_point
). Aduration
has a representation which holds a count of ticks, and a tick period. The tick period is the amount of time which occurs from one tick to another in units of a second. It is expressed as a rational constant usingratio
.template <class Rep, class Period = ratio<1>> class duration { public: typedef Rep rep; typedef Period period; private: rep rep_; // exposition only public: // construction / destruction duration() = default; template <class Rep2> explicit duration(const Rep2& r); ~duration() = default; // copy semantics duration(const duration&) = default; duration& operator=(const duration&) = default; // conversions template <class Rep2, class Period2> duration(const duration<Rep2, Period2>& d); // observer rep count() const; // arithmetic duration operator+() const; duration operator-() const; duration& operator++(); duration operator++(int); duration& operator--(); duration operator--(int); duration& operator+=(const duration& d); duration& operator-=(const duration& d); duration& operator*=(const rep& rhs); duration& operator/=(const rep& rhs); // special values static constexpr duration zero(); static constexpr duration min(); static constexpr duration max(); };
Rep
shall be an arithmetic type, or a class emulating an arithmetic type. Ifduration
is instantiated with the type ofRep
being aduration
, a diagnostic is required.
Period
shall be an instantiation ofratio
, diagnostic required.
Period::num
shall be positive, diagnostic required.Examples:
duration<long, ratio<60>>
holds a count of minutes using along
.
duration<long long, milli>
holds a count of milliseconds using along long
.
duration<double, ratio<1, 30>>
holds a count using adouble
with a tick period of 1/30 second (a tick frequency of 30 Hz).
The following members of
duration
do not throw an exception unless the indicated operations on the representations throw an exception.
duration
constructorstemplate <class Rep2> explicit duration(const Rep2& r);Requires:
Rep2
is implicitly convertible torep
, and
treat_as_floating_point<rep>::value
istrue
, or!treat_as_floating_point<rep>::value && !treat_as_floating_point<Rep2>::value
istrue
.A diagnostic is required if this requirement is not met. [Note: This requirement prevents construction of an integral-based
duration
with a floating point representation. Such a construction could easily lead to confusion about the value of theduration
. -- end note]Example:
duration<int, milli> d(3.5); // shall not compile duration<int, milli> d(3); // okEffects: Constructs an object of type
duration
.PostConditions:
count() == static_cast<rep>(r)
.template <class Rep2, class Period2> duration(const duration<Rep2, Period2>& d);Requires:
treat_as_floating_point<rep>::value
, orratio_divide<Period2, period>::type::den == 1
.A diagnostic is required if this requirement is not met. [Note: This requirement prevents implicit truncation error when converting between integral-based
duration
s. Such a construction could easily lead to confusion about the value of theduration
. -- end note]Example:
duration<int, milli> ms(3); duration<int, micro> us = ms; // ok duration<int, milli> ms2 = us; // shall not compileEffects: Constructs an object of type
duration
, constructingrep_
fromduration_cast<duration>(d).count()
.
duration
observersrep count() const;Returns:
rep_
.
duration
member arithmeticduration operator+() const;Returns:
*this
.duration operator-() const;Returns:
duration(-rep_)
.duration& operator++();Effects:
++rep_
.Returns:
*this
.duration operator++(int);Returns:
duration(rep_++)
.duration& operator--();Effects:
--rep_
.Returns:
*this
.duration operator--(int);Returns:
duration(rep_--)
.duration& operator+=(const duration& d);Effects:
rep_ += d.count()
.Returns:
*this
.duration& operator-=(const duration& d);Effects:
rep_ -= d.count()
.Returns:
*this
.duration& operator*=(const rep& rhs);Effects:
rep_ *= rhs
.Returns:
*this
.duration& operator/=(const rep& rhs);Effects:
rep_ /= rhs
.Returns:
*this
.
duration
special valuesstatic constexpr duration zero();Returns:
duration(duration_values<rep>::zero())
.static constexpr duration min();Returns:
duration(duration_values<rep>::min())
.static constexpr duration max();Returns:
duration(duration_values<rep>::max())
.
duration
non-member arithmetictemplate <class Rep1, class Period1, class Rep2, class Period2> typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:CD(lhs) += rhs
whereCD
is the type of the return value.template <class Rep1, class Period1, class Rep2, class Period2> typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:CD(lhs) -= rhs
whereCD
is the type of the return value.template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator*(const duration<Rep1, Period>& d, const Rep2& s);Requires: Let
CR
represent thecommon_type
ofRep1
andRep2
. BothRep1
andRep2
shall be implicitly convertible toCR
, diagnostic required.Returns:
duration<CR, Period>(d) *= s
.template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator*(const Rep1& s, const duration<Rep2, Period>& d);Requires: Let
CR
represent thecommon_type
ofRep1
andRep2
. BothRep1
andRep2
shall be implicitly convertible toCR
, diagnostic required.Returns:
d * s
.template <class Rep1, class Period, class Rep2> duration<typename common_type<Rep1, Rep2>::type, Period> operator/(const duration<Rep1, Period>& d, const Rep2& s);Requires: Let
CR
represent thecommon_type
ofRep1
andRep2
. BothRep1
andRep2
shall be implicitly convertible toCR
, andRep2
shall not be an instantiation ofduration
, diagnostic required.Returns:
duration<CR, Period>(d) /= s
.template <class Rep1, class Period1, class Rep2, class Period2> typename common_type<Rep1, Rep2>::type operator/(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns: Let
CD
represent thecommon_type
of the twoduration
arguments. ReturnsCD(lhs).count() / CD(rhs).count()
.
duration
comparisonstemplate <class Rep1, class Period1, class Rep2, class Period2> bool operator==(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns: LetCD
represent thecommon_type
of the twoduration
arguments. ReturnsCD(lhs).count() == CD(rhs).count()
template <class Rep1, class Period1, class Rep2, class Period2> bool operator!=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:!(lhs == rhs)
.template <class Rep1, class Period1, class Rep2, class Period2> bool operator< (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns: LetCD
represent thecommon_type
of the twoduration
arguments. ReturnsCD(lhs).count() < CD(rhs).count()
template <class Rep1, class Period1, class Rep2, class Period2> bool operator<=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:!(rhs < lhs)
.template <class Rep1, class Period1, class Rep2, class Period2> bool operator> (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:rhs < lhs
.template <class Rep1, class Period1, class Rep2, class Period2> bool operator>=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);Returns:!(lhs < rhs)
.
duration_cast
template <class ToDuration, class Rep, class Period> ToDuration duration_cast(const duration<Rep, Period>& d);Requires:
ToDuration
is an instantiation ofduration
, diagnostic required.Returns: Forms
CF
which is aratio
resulting fromratio_divide<Period, typename ToDuration::period>::type
. LetCR
be thecommon_type
ofToDuration::rep
,Rep
, andintmax_t
.
If
CF::num == 1
andCF::den == 1
, then returnsToDuration(static_cast<typename ToDuration::rep>(d.count()))
else if
CF::num != 1
andCF::den == 1
, then returnsToDuration(static_cast<typename ToDuration::rep>(static_cast<CR>(d.count()) * static_cast<CR>(CF::num)))else if
CF::num == 1
andCF::den != 1
, then returnsToDuration(static_cast<typename ToDuration::rep>(static_cast<CR>(d.count()) / static_cast<CR>(CF::den)))else returns
ToDuration(static_cast<typename ToDuration::rep>(static_cast<CR>(d.count()) * static_cast<CR>(CF::num) / static_cast<CR>(CF::den)))Remarks: This function shall not rely on any implicit conversions. All conversions shall be accomplished through
static_cast
. The implementation shall avoid all multiplications or divisions when it is known at compile time that it can be avoided because one or more arguments are 1. All intermediate computations shall be carried out in the widest possible representation and only converted to the destination representation at the final step.Class template
time_point
[chrono.time.point]A
time_point
represents a point in time with respect to a specific clock.Class template
time_point
template <class Clock, class Duration = typename Clock::duration> class time_point { public: typedef Clock clock; typedef Duration duration; typedef typename duration::rep rep; typedef typename duration::period period; private: duration d_; // exposition only public: time_point(); // has value "epoch" explicit time_point(const duration& d); // same as time_point() + d // conversions template <class Duration2> time_point(const time_point<clock, Duration2>& t); // observer duration time_since_epoch() const; // arithmetic time_point& operator+=(const duration& d); time_point& operator-=(const duration& d); // special values static constexpr time_point min(); static constexpr time_point max(); };
Clock
shall meet the Clock Requirements [chrono.clock.req].
Duration
shall be an instantiation ofduration
, diagnostic required.
time_point
constructorstime_point();Effects: Constructs an object oftime_point
, initializingd_
withduration::zero()
. Thistime_point
represents the epoch.time_point(const duration& d);Effects: Constructs an object oftime_point
, initializingd_
withd
. Thistime_point
represents the epoch+ d
.template <class Duration2> time_point(const time_point<clock, Duration2>& t);Requires:
Duration2
shall be implicitly convertible toduration
, diagnostic required.Effects: Constructs an object of
time_point
, initializingd_
witht.time_since_epoch()
.
time_point
observersduration time_since_epoch() const;Returns:
d_
.
time_point
member arithmetictime_point& operator+=(const duration& d);Effects:
d_ += d
.Returns:
*this
.time_point& operator-=(const duration& d);Effects:
d_ -= d
.Returns:
*this
.
time_point
special valuesstatic constexpr time_point min();Returns:time_point(duration::min())
.static constexpr time_point max();Returns:time_point(duration::max())
.
time_point
non-member arithmetictemplate <class Clock, class Duration1, class Rep2, class Period2> time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type> operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);Returns:CT(lhs) += rhs
whereCT
is the type of the return value.template <class Rep1, class Period1, class Clock, class Duration2> time_point<Clock, typename common_type<duration<Rep1, Period1>, Duration2>::type> operator+(const duration<Rep1, Period1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:rhs + lhs
.template <class Clock, class Duration1, class Rep2, class Period2> time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type> operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);Returns:lhs + (-rhs)
.template <class Clock, class Duration1, class Duration2> typename common_type<Duration1, Duration2>::type operator-(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:lhs.time_since_epoch() - rhs.time_since_epoch()
.
time_point
comparisonstemplate <class Clock, class Duration1, class Duration2> bool operator==(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:lhs.time_since_epoch() == rhs.time_since_epoch()
.template <class Clock, class Duration1, class Duration2> bool operator!=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:!(lhs == rhs)
.template <class Clock, class Duration1, class Duration2> bool operator< (const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:lhs.time_since_epoch() < rhs.time_since_epoch()
.template <class Clock, class Duration1, class Duration2> bool operator<=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:!(rhs < lhs)
.template <class Clock, class Duration1, class Duration2> bool operator> (const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:rhs < lhs
.template <class Clock, class Duration1, class Duration2> bool operator>=(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs);Returns:!(lhs < rhs)
.
time_point_cast
template <class ToDuration, class Clock, class Duration> time_point<Clock, ToDuration> time_point_cast(const time_point<Clock, Duration>& t);Requires:
ToDuration
is an instantiation ofduration
, diagnostic required.Returns:
time_point<Clock, ToDuration>(duration_cast<ToDuration>(t.time_since_epoch()))
.Clocks [chrono.clock]
The types defined in this section shall satisfy the Clock Requirements [chrono.clock.req].
system_clock
Class
system_clock
represents 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 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); };
system_clock::duration::min() < system_clock::duration::zero()
shall betrue
.time_t to_time_t(const time_point& t);Returns: Atime_t
such that thetime_t
andt
represent the same point in time, truncated to the courser of the precisions amongtime_t
andt
.time_point from_time_t(time_t t);Returns: Atime_point
such that thetime_point
andt
represent the same point in time, truncated to the courser of the precisions amongtime_point
andt
.
monotonic_clock
monotonic_clock
represents a clock for which thetime_point
never decreases as physical time advances. This type is conditionally supported: if not provided, classmonotonic_clock
shall not be declared.monotonic_clock
is permitted to be a separate type or atypedef
ofsystem_clock
.class monotonic_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 = true; static time_point now(); };
high_resolution_clock
Class
high_resolution_clock
represents the clock with the shortest tickperiod
.high_resolution_clock
is permitted to be a separate type or atypedef
ofsystem_clock
ormonotonic_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, duration> time_point; static const bool is_monotonic = <unspecified>; static time_point now(); };Threads
The current time-related threading API is adapted to this proposal. An emphasis is put on ease of use, safety, efficiency, and durability with respect to advancing technology over time.
Summary of changes:
- The parameters of the time-related functions have been modified to match the time API in this proposal.
sleep(rel_time)
has been renamed tosleep_for(rel_time)
.sleep(abs_time)
has been renamed tosleep_until(abs_time)
.timed_lock(rel_time)
has been renamed totry_lock_for(rel_time)
.timed_lock(abs_time)
has been renamed totry_lock_until(abs_time)
.timed_wait(rel_time)
has been renamed towait_for(rel_time)
timed_wait(abs_time)
has been renamed towait_until(abs_time)
.- The remark constraining the
unique_lock
constructor taking aDuration
has been removed (it is no longer needed).- A preference for the use of a monotonic clock has been stated for the
*_for
functions.Threads Proposed Wording
Modify 30.2 [thread.threads]:
template <class Clock, class Duration> void sleep_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); template <classDurationRep, class Period> void sleep_for(constDurationchrono::duration<Rep, Period>& rel_time);Modify 30.2.2 [thread.thread.this]:
template <class Clock, class Duration> void sleep_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); template <classDurationRep, class Period> void sleep_for(constDurationchrono::duration<Rep, Period>& rel_time); ... template <class Clock, class Duration> void this_thread::sleep_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); ... template <classDurationRep, class Period> void sleep_for(constDurationchrono::duration<Rep, Period>& rel_time);...
[Note: Implementations should use a monotonic clock for measuring
rel_time
if available. -- end note]Modify 30.3.2 [thread.timedmutex.requirements]:
A
TimedMutex
type shall meet the requirements for aMutex
type. In addition, it shall meet the requirements set out in this clause 30.3.2, whererel_time
denotesa value of a typean instantiation ofRelTime
that meets theDuration
duration
(??)requirementsandabs_time
denotesa value of typean instantiation ofstd::system_time
time_point
.The expression
m.
shall be well-formed and have the following semantics:timed_locktry_lock_for(rel_time)Precondition: If the
resolutiontickperiod
ofis
RelTimerel_timefiner thannot exactly convertible to the nativeresolutiontickperiod
, thetimeduration
shall be rounded up to the nearest nativeresolutiontickperiod
.Effects: The function attempts to obtain ownership of the mutex within the time specified by
rel_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 the time 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. Implementations should use a monotonic clock for measuringrel_time
if available. -- end note]Return type:
bool
Returns:
true
if ownership was obtained, otherwisefalse
.Synchronization: If
returns
timed_locktry_lock_for()true
, priorunlock()
operations on the same object synchronize with (1.10) this operation.Throws: Nothing.
The expression
m.
shall be well-formed and have the following semantics:timed_locktry_lock_until(abs_time)Effects: The function attempts to obtain ownership of the mutex by the time specified by
abs_time
. Ifabs_time
has already passed, the function attempts to obtain ownership without blocking (as if by callingtry_lock()
). The function shall return before the time 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]Return type:
bool
Returns:
true
if ownership was obtained, otherwisefalse
.Synchronization: If
returns
timed_locktry_lock_until()true
, priorunlock()
operations on the same object synchronize with (1.10) this operation.Throws: Nothing.
Modify 30.3.2.1 [thread.timedmutex.class]:
namespace std { class timed_mutex { public: timed_mutex(); ~timed_mutex(); timed_mutex(const timed_mutex&) = delete; timed_mutex& operator=(const timed_mutex&) = delete; void lock(); bool try_lock(); template <classDurationRep, class Period> booltimed_locktry_lock_for(constDurationchrono::duration<Rep, Period>& rel_time); template <class Clock, class Duration> booltimed_locktry_lock_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); void unlock(); typedef implementation-defined native_handle_type; // See 30.1.3 native_handle_type native_handle(); // See 30.1.3 }; }The class
timed_mutex
provides a non-recursive mutex with exclusive ownership semantics. If one thread owns atimed_mutex
object, attempts by another thread to acquire ownership of that object will fail (fortry_lock()
) or block (forlock()
,try_lock_for()
and) until the owning thread has released ownership with a call to
timed_locktry_lock_until()unlock()
or the call toor
timed_locktry_lock_for()try_lock_until()
times out (having failed to obtain ownership).The class
timed_mutex
shall satisfy all of theTimedMutex
requirements (30.3.2). It shall be a standard-layout class (9).The behavior of a program is undefined if:
- it destroys a
timed_mutex
object owned by any thread,- a thread that owns a
timed_mutex
object callslock()
,try_lock()
,try_lock_for()
ortry_lock_until()
or either overload ofon that object, ortimed_lock()
- a thread terminates while owning a
timed_mutex
object.Modify 30.3.2.2 [thread.timedmutex.recursive]:
namespace std { class recursive_timed_mutex { public: recursive_timed_mutex(); ~recursive_timed_mutex(); recursive_timed_mutex(const recursive_timed_mutex&) = delete; recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete; void lock(); bool try_lock(); template <classDurationRep, class Period> booltimed_locktry_lock_for(constDurationchrono::duration<Rep, Period>& rel_time); template <class Clock, class Duration> booltimed_locktry_lock_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); void unlock(); typedef implementation-defined native_handle_type; // See 30.1.3 native_handle_type native_handle(); // See 30.1.3 }; }The class
recursive_timed_mutex
provides a non-recursive mutex with exclusive ownership semantics. If one thread owns arecursive_timed_mutex
object, attempts by another thread to acquire ownership of that object will fail (fortry_lock()
) or block (forlock()
,try_lock_for()
and) until the owning thread has released ownership with a call to
timed_locktry_lock_until()unlock()
or the call toor
timed_locktry_lock_for()try_lock_until()
times out (having failed to obtain ownership).The class
recursive_timed_mutex
shall satisfy all of theTimedMutex
requirements (30.3.2). It shall be a standard-layout class (9).A thread that owns a
recursive_timed_mutex
object may acquire additional levels of ownership by callinglock()
,try_lock()
, try_lock_for() ortimed_lock()try_lock_until() on that object. It is unspecified how many levels of ownership may be acquired by a single thread. If a thread has already acquired the maximum level of ownership for arecursive_timed_mutex
object, additional calls totry_lock()
, try_lock_for() ortimed_lock()try_lock_until() shall fail, and additional calls tolock()
shall throw an exception of type std::system_error. A thread shall callunlock()
once for each level of ownership acquired by calls tolock()
,try_lock()
, try_lock_for() andtimed_lock()try_lock_until(). Only when all levels of ownership have been released may ownership of the object be acquired by another thread.The behavior of a program is undefined if:
- it destroys a
recursive_timed_mutex
object owned by any thread, or- a thread terminates while owning a
recursive_timed_mutex
object.Modify 30.3.3.2 [thread.lock.unique]:
namespace std { template <class Mutex> class unique_lock { public: typedef Mutex mutex_type; // 30.3.3.2.1 construct/copy/destroy unique_lock(); explicit unique_lock(mutex_type& m); unique_lock(mutex_type& m, defer_lock_t); unique_lock(mutex_type& m, try_to_lock_t); unique_lock(mutex_type& m, adopt_lock_t); template <class Clock, class Duration> unique_lock(mutex_type& m, constsystem_timechrono::time_point<Clock, Duration>& abs_time); template <classDurationRep, class Period> unique_lock(mutex_type& m, constDurationchrono::duration<Rep, Period>& rel_time); ~unique_lock(); unique_lock(unique_lock const&) = delete; unique_lock& operator=(unique_lock const&) = delete; unique_lock(unique_lock&& u); unique_lock& operator=(unique_lock&& u); // 30.3.3.2.2 locking void lock(); bool try_lock(); template <classDurationRep, class Period> booltimedtry_lock_for(constDurationchrono::duration<Rep, Period>& rel_time); template <class Clock, class Duration> booltimedtry_lock_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time); void unlock(); // 30.3.3.2.3 modifiers void swap(unique_lock&& u); mutex_type* release(); // 30.3.3.2.4 observers bool owns_lock() const; explicit operator bool () const; mutex_type* mutex() const; private: // exposition only: mutex_type* pm; bool owns; }; ...Modify 30.3.3.2.1 [thread.lock.unique.cons]:
template <class Clock, class Duration> unique_lock(mutex_type& m, constsystem_timechrono::time_point<Clock, Duration>& abs_time);Precondition: If
mutex_type
is not a recursive mutex the calling thread does not own the mutex.Effects: Constructs an object of type
unique_lock
and callsm.
.timedtry_lock_until(abs_time)Postconditions:
pm == &m
andowns == res
, whereres
is the value returned by the call to.
timedtry_lock_until(abs_time)Throws: Nothing.
template <classDurationRep, class Period> unique_lock(mutex_type& m, constDurationchrono::duration<Rep, Period>& rel_time);
Remark: The implementation shall ensure that onlyDuration
types (") will bind to this constructor.Precondition: If
mutex_type
is not a recursive mutex the calling thread does not own the mutex.Effects: Constructs an object of type
unique_lock
and callsm.
.timedtry_lock_for(rel_time)Postconditions:
pm == &m
andowns == res
, whereres
is the value returned by the call tom.
.timedtry_lock_for(rel_time)Throws: Nothing.
Modify 30.3.3.2.2 [thread.lock.unique.locking]:
template <class Clock, class Duration> booltimedtry_lock_until(constsystem_timechrono::time_point<Clock, Duration>& abs_time);Effects:
pm->
timedtry_lock_until(abs_time)Returns: The value returned by the call to
.
timedtry_lock_until(abs_time)Postcondition:
owns == res
, whereres
is the value returned by the call to.
timedtry_lock_until(abs_time)Throws:
lock_error
if on entryowns
istrue
orpm
is null.template <classDurationRep, class Period> booltimedtry_lock_for(constDurationchrono::duration<Rep, Period>& rel_time);Effects:
pm->
timedtry_lock_for(rel_time)Returns: The value returned by the call to
.
timedtry_lock_for(rel_time)Postcondition:
owns == res
, whereres
is the value returned by the call to.
timedtry_lock_for(rel_time)Throws:
lock_error
if on entryowns
is true orpm
is null.Modify 30.4 [thread.condition]:
...
Condition variables permit concurrent invocation of the
wait
,,
timed_wait_for,
timed_wait_untilnotify_one
andnotify_all
member functions....
The implementation shall behave as if
notify_one
,notify_all
, and each part of thewait
, wait_for andexecutions are executed in some unspecified total order.
timed_wait_until...
Modify 30.4.1 [thread.condition.condvar]:
namespace std { class condition_variable { public: condition_variable(); ~condition_variable(); condition_variable(const condition_variable&) = delete; condition_variable& operator=(const condition_variable&) = delete; void notify_one(); void notify_all(); void wait(unique_lock<mutex>& lock); template <class Predicate> void wait(unique_lock<mutex>& lock, Predicate pred);template <class Duration> bool timed_wait(unique_lock<mutex>& lock, const Duration& rel_time); bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time); template <class Predicate> bool timed_wait(unique_lock<mutex>& lock, const system_time& abs_time, Predicate pred); template <class Duration, class Predicate> bool timed_wait(unique_lock<mutex>& lock, const Duration& rel_time, Predicate pred);template <class Clock, class Duration> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time); template <class Clock, class Duration, class Predicate> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred); template <class Rep, class Period> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time); template <class Rep, class Period, class Predicate> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred); typedef implementation-defined native_handle_type; // See 30.1.3 native_handle_type native_handle(); // See 30.1.3 }; }...
template <class Clock, class Duration> booltimed_wait_until(unique_lock<mutex>& lock, constsystem_timechrono::time_point<Clock, Duration>& abs_time);Precondition:
lock
is locked by the calling thread, and either
- no other thread is waiting on this
condition_variable
object orlock.mutex()
returns the same value for each of thelock
arguments supplied by all concurrently waiting threads (viawait
,wait_for
or).
timed_wait_untilEffects:
- Atomically calls
lock.unlock()
and blocks on*this
.- When unblocked, calls
lock.lock()
(possibly blocking on thelock
) and returns.- The function will unblock when signaled by a call to
notify_one()
, a call tonotify_all()
, by the current time exceedingabs_time
, or spuriously.- If the function exits via an exception,
lock.unlock()
shall be called prior to exiting the function scope.Postcondition:
lock
is locked by the calling thread.Returns:
Clock::now() < abs_time
.false
if the call is returning because the time specified byabs_time
was reached, otherwisetrue
Throws:
std::system_error
when the returned value, effects, or postcondition cannot be achieved.template <class Clock, class Duration, class Predicate> booltimed_wait_until(unique_lock<mutex>& lock, constsystem_timechrono::time_point<Clock, Duration>& abs_time, Predicate pred);Effects:
while (!pred()) if (!timed_wait_until(lock, abs_time)) return pred(); return true;...
template <classDurationRep, class Period> booltimed_wait_for(unique_lock<mutex>& lock, constDurationchrono::duration<Rep, Period>& rel_time);Effects: As if
timed_wait_until(lock,std::get_system_time()chrono::monotonic_clock::now() + rel_time)Returns:
false
if the call is returning because the time duration specified byrel_time
has elapsed, otherwisetrue
.Note: A monotonic clock is preferred but not required for measuring
rel_time
in this function.template <classDurationRep, class Period, class Predicate> booltimed_wait_for(unique_lock<mutex>& lock, constDurationchrono::duration<Rep, Period>& rel_time, Predicate pred);Effects:
timed_wait_until(lock,std::get_system_time()chrono::monotonic_clock::now() + rel_time, std::move(pred))[Note: There is no blocking if
pred()
is initiallytrue
, even if the timeout has already expired. A monotonic clock is preferred but not required for measuringrel_time
in this function. -- end note]...
Modify 30.4.2 [thread.condition.condvarany]:
namespace std { class condition_variable_any { public: condition_variable_any(); ~condition_variable_any(); condition_variable_any(const condition_variable_any&) = delete; condition_variable_any& operator=(const condition_variable_any&) = delete; void notify_one(); void notify_all(); template <class Lock> void wait(Lock& lock); template <class Lock, class Predicate> void wait(Lock& lock, Predicate pred);template <class Lock> bool timed_wait(Lock& lock, const system_time& abs_time); template <class Lock, class Duration> bool timed_wait(Lock& lock, const Duration& rel_time); template <class Lock, class Predicate> bool timed_wait(Lock& lock, const system_time& abs_time, Predicate pred); template <class Lock, class Duration, class Predicate> bool timed_wait(Lock& lock, const Duration& rel_time, Predicate pred);template <class Lock, class Clock, class Duration> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time); template <class Lock, class Clock, class Duration, class Predicate> bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred); template <class Lock, class Rep, class Period> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time); template <class Lock, class Rep, class Period, class Predicate> bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred); typedef implementation-defined native_handle_type; // See 30.1.3 native_handle_type native_handle(); // See 30.1.3 }; }...
template <class Lock, class Clock, class Duration> booltimed_wait_until(Lock& lock, constsystem_timechrono::time_point<Clock, Duration>& abs_time);Effects:
- Atomically calls
lock.unlock()
and blocks on*this
.- When unblocked, calls
lock.lock()
(possibly blocking on thelock
) and returns.- The function will unblock when signaled by a call to
notify_one()
, a call tonotify_all()
, by the current time exceedingabs_time
, or spuriously.- If the function exits via an exception,
lock.unlock()
shall be called prior to exiting the function scope.Postcondition:
lock
is locked by the calling thread.Returns:
Clock::now() < abs_time
.false
if the call is returning because the time specified byabs_time
was reached, otherwisetrue
Throws:
std::system_error
when the returned value, effects, or postcondition cannot be achieved.template <class Lock, class Clock, class Duration, class Predicate> booltimed_wait_until(Lock& lock, constsystem_timechrono::time_point<Clock, Duration>& abs_time, Predicate pred);Effects:
while (!pred()) if (!timed_wait_until(lock, abs_time)) return pred(); return true;...
template <class Lock, classDurationRep, class Period> booltimed_wait_for(Lock& lock, constDurationchrono::duration<Rep, Period>& rel_time);Effects: As if
timed_wait_until(lock,std::get_system_time()chrono::monotonic_clock::now() + rel_time)Returns:
false
if the call is returning because the time duration specified byrel_time
has elapsed, otherwisetrue
.Note: A monotonic clock is preferred but not required for measuring
rel_time
in this function.template <class Lock, classDurationRep, class Period, class Predicate> booltimed_wait_for(Lock& lock, constDurationchrono::duration<Rep, Period>& rel_time, Predicate pred);Effects:
timed_wait_until(lock,std::get_system_time()chrono::monotonic_clock::now() + rel_time, std::move(pred))[Note: There is no blocking if
pred()
is initiallytrue
, even if the timeout has already expired. A monotonic clock is preferred but not required for measuringrel_time
in this function. -- end note]...
Acknowledgements
The help of many people in preparing the technical background of this paper is much appreciated. Special thanks to Andrei Alexandrescu, Lawrence Crowl, Beman Dawes, Peter Dimov, Terry Golubiewski, Daniel Krügler and Anthony Williams.