692 changes: 567 additions & 125 deletions src/core/time.d
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,209 @@ version(unittest) T copy(T)(T t)
}


/++
What type of clock to use with $(LREF MonoTime) / $(LREF MonoTimeImpl) or
$(D std.datetime.Clock.currTime). They default to $(D ClockType.normal),
and most programs do not need to ever deal with the others.
The other $(D ClockType)s are provided so that other clocks provided by the
underlying C, system calls can be used with $(LREF MonoTimeImpl) or
$(D std.datetime.Clock.currTime) without having to use the C API directly.
In the case of the monotonic time, $(LREF MonoTimeImpl) is templatized on
$(D ClockType), whereas with $(D std.datetime.Clock.currTime), its a runtime
argument, since in the case of the monotonic time, the type of the clock
affects the resolution of a $(LREF MonoTimeImpl) object, whereas with
$(D XREF datetime, SysTime), its resolution is always hecto-nanoseconds
regardless of the source of the time.
$(D ClockType.normal), $(D ClockType.coarse), and $(D ClockType.precise)
work with both $(D Clock.currTime) and $(LREF MonoTimeImpl).
$(D ClockType.second) only works with $(D Clock.currTime). The others only
work with $(LREF MonoTimeImpl).
+/
version(CoreDdoc) enum ClockType
{
/++
Use the normal clock.
+/
normal = 0,

/++
$(BLUE Linux-Only)
Uses $(D CLOCK_MONOTONIC_BOOTTIME).
+/
bootTime = 1,

/++
Use the coarse clock, not the normal one (e.g. on Linux, that would be
$(D CLOCK_REALTIME_COARSE) instead of $(D CLOCK_REALTIME) for
$(D clock_gettime) if a function is using the realtime clock). It's
generally faster to get the time with the coarse clock than the normal
clock, but it's less precise (e.g. 1 msec instead of 1 usec or 1 nsec).
Howeover, it $(I is) guaranteed to still have sub-second precision
(just not as high as with $(D ClockType.normal)).
On systems which do not support a coarser clock,
$(D MonoTimeImpl!(ClockType.coarse)) will internally use the same clock
as $(D Monotime) does, and $(D Clock.currTime!(ClockType.coarse)) will
use the same clock as $(D Clock.currTime). This is because the coarse
clock is doing the same thing as the normal clock (just at lower
precision), whereas some of the other clock types
(e.g. $(D ClockType.processCPUTime)) mean something fundamentally
different. So, treating those as $(D ClockType.normal) on systems where
they weren't natively supported would give misleading results.
Most programs should not use the coarse clock, exactly because it's
less precise, and most programs don't need to get the time often
enough to care, but for those rare programs that need to get the time
extremely frequently (e.g. hundreds of thousands of times a second) but
don't care about high precision, the coarse clock might be appropriate.
Currently, only Linux and FreeBSD support a coarser clock, and on other
platforms, it's treated as $(D ClockType.normal).
+/
coarse = 2,

/++
Uses a more precise clock than the normal one (which is already very
precise), but it takes longer to get the time. Similarly to
$(D ClockType.coarse), if it's used on a system that does not support a
more precise clock than the normal one, it's treated as equivalent to
$(D ClockType.normal).
Currently, only FreeBSD supports a more precise clock, where it uses
$(D CLOCK_MONOTONIC_PRECISE) for the monotonic time and
$(D CLOCK_REALTIME_PRECISE) for the wall clock time.
+/
precise = 3,

/++
$(BLUE Linux-Only)
Uses $(D CLOCK_PROCESS_CPUTIME_ID).
+/
processCPUTime = 4,

/++
$(BLUE Linux-Only)
Uses $(D CLOCK_MONOTONIC_RAW).
+/
raw = 5,

/++
Uses a clock that has a precision of one second (contrast to the coarse
clock, which has sub-second precision like the normal clock does).
FreeBSD is the only system which specifically has a clock set up for
this (it has $(D CLOCK_SECOND) to use with $(D clock_gettime) which
takes advantage of an in-kernel cached value), but on other systems, the
fastest function available will be used, and the resulting $(D SysTime)
will be rounded down to the second if the clock that was used gave the
time at a more precise resolution. So, it's guaranteed that the time
will be given at a precision of one second and it's likely the case that
will be faster than $(D ClockType.normal), since there tend to be
several options on a system to get the time at low resolutions, and they
tend to be faster than getting the time at high resolutions.
So, the primary difference between $(D ClockType.coarse) and
$(D ClockType.second) is that $(D ClockType.coarse) sacrifices some
precision in order to get speed but is still fairly precise, whereas
$(D ClockType.second) tries to be as fast as possible at the expense of
all sub-second precision.
+/
second = 6,

/++
$(BLUE Linux-Only)
Uses $(D CLOCK_THREAD_CPUTIME_ID).
+/
threadCPUTime = 7,

/++
$(BLUE FreeBSD-Only)
Uses $(D CLOCK_UPTIME).
+/
uptime = 8,

/++
$(BLUE FreeBSD-Only)
Uses $(D CLOCK_UPTIME_FAST).
+/
uptimeCoarse = 9,

/++
$(BLUE FreeBSD-Only)
Uses $(D CLOCK_UPTIME_PRECISE).
+/
uptimePrecise = 10,
}
else version(Windows) enum ClockType
{
normal = 0,
coarse = 2,
precise = 3,
second = 6,
}
else version(OSX) enum ClockType
{
normal = 0,
coarse = 2,
precise = 3,
second = 6,
}
else version(linux) enum ClockType
{
normal = 0,
bootTime = 1,
coarse = 2,
precise = 3,
processCPUTime = 4,
raw = 5,
second = 6,
threadCPUTime = 7,
}
else version(FreeBSD) enum ClockType
{
normal = 0,
coarse = 2,
precise = 3,
second = 6,
uptime = 8,
uptimeCoarse = 9,
uptimePrecise = 10,
}
else
{
// It needs to be decided (and implemented in an appropriate version branch
// here) which clock types new platforms are going to support. At minimum,
// the ones _not_ marked with $(D Blue Foo-Only) should be supported.
static assert(0, "What are the clock types supported by this system?");
}

unittest
{
// Make sure that the values are the same across platforms.
static if(is(typeof(ClockType.normal))) static assert(ClockType.normal == 0);
static if(is(typeof(ClockType.bootTime))) static assert(ClockType.bootTime == 1);
static if(is(typeof(ClockType.coarse))) static assert(ClockType.coarse == 2);
static if(is(typeof(ClockType.precise))) static assert(ClockType.precise == 3);
static if(is(typeof(ClockType.processCPUTime))) static assert(ClockType.processCPUTime == 4);
static if(is(typeof(ClockType.raw))) static assert(ClockType.raw == 5);
static if(is(typeof(ClockType.second))) static assert(ClockType.second == 6);
static if(is(typeof(ClockType.threadCPUTime))) static assert(ClockType.threadCPUTime == 7);
static if(is(typeof(ClockType.uptime))) static assert(ClockType.uptime == 8);
static if(is(typeof(ClockType.uptimeCoarse))) static assert(ClockType.uptimeCoarse == 9);
static if(is(typeof(ClockType.uptimePrecise))) static assert(ClockType.uptimePrecise == 10);
}


/++
Represents a duration of time of weeks or less (kept internally as hnsecs).
(e.g. 22 days or 700 seconds).
Expand Down Expand Up @@ -1848,6 +2051,14 @@ unittest
}


/++
alias for $(D MonoTimeImpl) instantiated with $(D ClockType.normal). This is
what most programs should use. It's also what much of $(D MonoTimeImpl) uses
in its documentation (particularly in the examples), because that's what's
going to be used in most code.
+/
alias MonoTime = MonoTimeImpl!(ClockType.normal);

/++
Represents a timestamp of the system's monotonic clock.
Expand All @@ -1856,12 +2067,12 @@ unittest
$(XREF datetime, SysTime)). The system's wall clock time can be adjusted
by the user or by the system itself via services such as NTP, so it is
unreliable to use the wall clock time for timing. Timers which use the wall
clock time could easily end up never going off due changes made to the wall
clock time or otherwise waiting for a different period of time than that
specified by the programmer. However, because the monotonic clock always
increases at a fixed rate and is not affected by adjustments to the wall
clock time, it is ideal for use with timers or anything which requires high
precision timing.
clock time could easily end up never going off due to changes made to the
wall clock time or otherwise waiting for a different period of time than
that specified by the programmer. However, because the monotonic clock
always increases at a fixed rate and is not affected by adjustments to the
wall clock time, it is ideal for use with timers or anything which requires
high precision timing.
So, MonoTime should be used for anything involving timers and timing,
whereas $(XREF datetime, SysTime) should be used when the wall clock time
Expand All @@ -1877,19 +2088,108 @@ unittest
to get a meaningful duration of time. Normally, MonoTime does these
calculations for the programmer, but the $(D ticks) and $(D ticksPerSecond)
properties are provided for those who require direct access to the system
ticks. However, the normal way that MonoTime would be used is
ticks. The normal way that MonoTime would be used is
--------------------
MonoTime before = MonoTime.currTime;
// do stuff...
MonoTime after = MonoTime.currTime;
Duration timeElapsed = after - before;
MonoTime before = MonoTime.currTime;
// do stuff...
MonoTime after = MonoTime.currTime;
Duration timeElapsed = after - before;
--------------------
$(LREF MonoTime) is an alias to $(D MonoTimeImpl!(ClockType.normal)) and is
what most programs should use for the monotonic clock, so that's what is
used in most of $(D MonoTimeImpl)'s documentation. But $(D MonoTimeImpl)
can be instantiated with other clock types for those rare programs that need
it.
See_Also:
$(LREF ClockType)
+/
struct MonoTime
struct MonoTimeImpl(ClockType clockType)
{
// need this helper to get the index of the clock type in the array below
private static size_t _clockNameIndex()
{
foreach(i, strname; __traits(allMembers, ClockType))
{
mixin("alias val = ClockType." ~ strname ~ ";");
if(val == clockType)
return i;
}
assert(0);
}

private enum _clockIdx = _clockNameIndex();

unittest
{
mixin("alias val = ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~ ";");
assert(val == clockType);
}

@safe:

version(Windows)
{
static if(clockType != ClockType.coarse &&
clockType != ClockType.normal &&
clockType != ClockType.precise)
{
static assert(0, "ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~
" is not supported by MonoTimeImpl on this system.");
}
}
else version(OSX)
{
static if(clockType != ClockType.coarse &&
clockType != ClockType.normal &&
clockType != ClockType.precise)
{
static assert(0, "ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~
" is not supported by MonoTimeImpl on this system.");
}
}
else version(Posix)
{
version(linux)
{
import core.sys.linux.time;
static if(clockType == ClockType.bootTime) private alias clockArg = CLOCK_BOOTTIME;
else static if(clockType == ClockType.coarse) private alias clockArg = CLOCK_MONOTONIC_COARSE;
else static if(clockType == ClockType.normal) private alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.precise) private alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.processCPUTime) private alias clockArg = CLOCK_PROCESS_CPUTIME_ID;
else static if(clockType == ClockType.raw) private alias clockArg = CLOCK_MONOTONIC_RAW;
else static if(clockType == ClockType.threadCPUTime) private alias clockArg = CLOCK_THREAD_CPUTIME_ID;
else static assert(0, "ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~
" is not supported by MonoTimeImpl on this system.");
}
else version(FreeBSD)
{
import core.sys.freebsd.time;
static if(clockType == ClockType.coarse) private alias clockArg = CLOCK_MONOTONIC_FAST;
else static if(clockType == ClockType.normal) private alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.precise) private alias clockArg = CLOCK_MONOTONIC_PRECISE;
else static if(clockType == ClockType.uptime) private alias clockArg = CLOCK_UPTIME;
else static if(clockType == ClockType.uptimeCoarse) private alias clockArg = CLOCK_UPTIME_FAST;
else static if(clockType == ClockType.uptimePrecise) private alias clockArg = CLOCK_UPTIME_PRECISE;
else static assert(0, "ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~
" is not supported by MonoTimeImpl on this system.");
}
else
{
// It needs to be decided (and implemented in an appropriate
// version branch here) which clock types new platforms are
// going to support. Also, ClockType's documentation should be
// updated to mention it if a new platform uses anything that's
// not supported on all platforms..
static assert(0, "What are the monotonic clock types supported by this system?");
}
}
else
static assert(0, "Unsupported platform");

/++
The current time of the system's monotonic clock. This has no relation
to the wall clock time, as the wall clock time can be adjusted (e.g.
Expand All @@ -1905,12 +2205,15 @@ struct MonoTime
monotonic clock may indicate less time than has actually
passed if that occurs. This is known to happen on
Mac OS X. It has not been tested whether it occurs on
either Windows or on Linux.
either Windows or Linux.
+/
static @property MonoTime currTime() @trusted nothrow @nogc
static @property MonoTimeImpl currTime() @trusted nothrow @nogc
{
if(_ticksPerSecond == 0)
assert(0, "MonoTime failed to get the frequency of the system's monotonic clock.");
if(ticksPerSecond == 0)
{
assert(0, "MonoTimeImpl!(ClockType." ~ __traits(allMembers, ClockType)[_clockIdx] ~
") failed to get the frequency of the system's monotonic clock.");
}

version(Windows)
{
Expand All @@ -1920,19 +2223,19 @@ struct MonoTime
// This probably cannot happen on Windows 95 or later
assert(0, "Call to QueryPerformanceCounter failed.");
}
return MonoTime(ticks);
return MonoTimeImpl(ticks);
}
else version(OSX)
return MonoTime(mach_absolute_time());
return MonoTimeImpl(mach_absolute_time());
else version(Posix)
{
timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
if(clock_gettime(clockArg, &ts) != 0)
assert(0, "Call to clock_gettime failed.");

return MonoTime(convClockFreq(ts.tv_sec * 1_000_000_000L + ts.tv_nsec,
1_000_000_000L,
_ticksPerSecond));
return MonoTimeImpl(convClockFreq(ts.tv_sec * 1_000_000_000L + ts.tv_nsec,
1_000_000_000L,
ticksPerSecond));
}
}

Expand All @@ -1943,27 +2246,27 @@ struct MonoTime
A $(D MonoTime) of $(D 0) ticks. It's provided to be consistent with
$(D Duration.zero), and it's more explicit than $(D MonoTime.init).
+/
MonoTime zero() { return MonoTime(0); }
MonoTimeImpl zero() { return MonoTimeImpl(0); }

/++
Largest $(D MonoTime) possible.
+/
MonoTime max() { return MonoTime(long.max); }
MonoTimeImpl max() { return MonoTimeImpl(long.max); }

/++
Most negative $(D MonoTime) possible.
+/
MonoTime min() { return MonoTime(long.min); }
MonoTimeImpl min() { return MonoTimeImpl(long.min); }
}

unittest
{
assert(zero == MonoTime(0));
assert(MonoTime.max == MonoTime(long.max));
assert(MonoTime.min == MonoTime(long.min));
assert(MonoTime.min < MonoTime.zero);
assert(MonoTime.zero < MonoTime.max);
assert(MonoTime.min < MonoTime.max);
assert(MonoTimeImpl.zero == MonoTimeImpl(0));
assert(MonoTimeImpl.max == MonoTimeImpl(long.max));
assert(MonoTimeImpl.min == MonoTimeImpl(long.min));
assert(MonoTimeImpl.min < MonoTimeImpl.zero);
assert(MonoTimeImpl.zero < MonoTimeImpl.max);
assert(MonoTimeImpl.min < MonoTimeImpl.max);
}


Expand All @@ -1977,7 +2280,7 @@ struct MonoTime
$(TR $(TD this &gt; rhs) $(TD &gt; 0))
)
+/
int opCmp(MonoTime rhs) const pure nothrow @nogc
int opCmp(MonoTimeImpl rhs) const pure nothrow @nogc
{
if(_ticks < rhs._ticks)
return -1;
Expand All @@ -1986,23 +2289,23 @@ struct MonoTime

unittest
{
foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(U; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
T t = MonoTime.currTime;
T t = MonoTimeImpl.currTime;
U u = t;
assert(t == u);
assert(copy(t) == u);
assert(t == copy(u));
}
}

foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(U; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
T before = MonoTime.currTime;
T before = MonoTimeImpl.currTime;
auto after = U(before._ticks + 42);
assert(before < after);
assert(before <= before);
Expand All @@ -2021,20 +2324,20 @@ struct MonoTime
}
}

immutable currTime = MonoTime.currTime;
assert(MonoTime(long.max) > MonoTime(0));
assert(MonoTime(0) > MonoTime(long.min));
assert(MonoTime(long.max) > currTime);
assert(currTime > MonoTime(0));
assert(MonoTime(0) < currTime);
assert(MonoTime(0) < MonoTime(long.max));
assert(MonoTime(long.min) < MonoTime(0));
immutable currTime = MonoTimeImpl.currTime;
assert(MonoTimeImpl(long.max) > MonoTimeImpl(0));
assert(MonoTimeImpl(0) > MonoTimeImpl(long.min));
assert(MonoTimeImpl(long.max) > currTime);
assert(currTime > MonoTimeImpl(0));
assert(MonoTimeImpl(0) < currTime);
assert(MonoTimeImpl(0) < MonoTimeImpl(long.max));
assert(MonoTimeImpl(long.min) < MonoTimeImpl(0));
}


/++
Subtracting two MonoTimes results in a $(LREF Duration) representing the
amount of time which elapsed between them.
Subtracting two MonoTimes results in a $(LREF Duration) representing
the amount of time which elapsed between them.
The primary way that programs should time how long something takes is to
do
Expand Down Expand Up @@ -2066,7 +2369,7 @@ assert(before + timeElapsed == after).
MonoTime's $(D ticks) property and keep all calculations in ticks
rather than using $(LREF Duration).
+/
Duration opBinary(string op)(MonoTime rhs) const pure nothrow @nogc
Duration opBinary(string op)(MonoTimeImpl rhs) const pure nothrow @nogc
if(op == "-")
{
immutable diff = _ticks - rhs._ticks;
Expand All @@ -2075,21 +2378,21 @@ assert(before + timeElapsed == after).

unittest
{
foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(U; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
T t = MonoTime.currTime;
T t = MonoTimeImpl.currTime;
U u = t;
assert(u - t == Duration.zero);
assert(copy(t) - u == Duration.zero);
assert(t - copy(u) == Duration.zero);
}
}

foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(U; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
static void test()(T before, U after, Duration min, size_t line = __LINE__) @trusted
{
Expand All @@ -2107,9 +2410,9 @@ assert(before + timeElapsed == after).
if(before - after == -diff) {} else throw new AssertError("unittest failure 2", __FILE__, line);
}

T before = MonoTime.currTime;
test(before, MonoTime(before._ticks + 4202), Duration.zero);
test(before, MonoTime.currTime, Duration.zero);
T before = MonoTimeImpl.currTime;
test(before, MonoTimeImpl(before._ticks + 4202), Duration.zero);
test(before, MonoTimeImpl.currTime, Duration.zero);

auto durLargerUnits = dur!"minutes"(7) + dur!"seconds"(22);
test(before, before + durLargerUnits + dur!"msecs"(33) + dur!"hnsecs"(571), durLargerUnits);
Expand All @@ -2122,22 +2425,22 @@ assert(before + timeElapsed == after).
Adding or subtracting a $(LREF Duration) to/from a MonoTime results in
a MonoTime which is adjusted by that amount.
+/
MonoTime opBinary(string op)(Duration rhs) const pure nothrow @nogc
MonoTimeImpl opBinary(string op)(Duration rhs) const pure nothrow @nogc
if(op == "+" || op == "-")
{
immutable rhsConverted = convClockFreq(rhs._hnsecs, hnsecsPer!"seconds", ticksPerSecond);
mixin("return MonoTime(_ticks " ~ op ~ " rhsConverted);");
mixin("return MonoTimeImpl(_ticks " ~ op ~ " rhsConverted);");
}

unittest
{
foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(U; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(V; _TypeTuple!(Duration, const Duration, immutable Duration))
{
T t = MonoTime.currTime;
T t = MonoTimeImpl.currTime;
U u1 = t + V(0);
U u2 = t - V(0);
assert(t == u1);
Expand All @@ -2146,29 +2449,29 @@ assert(before + timeElapsed == after).
}
}

foreach(T; _TypeTuple!(MonoTime, const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(MonoTimeImpl, const MonoTimeImpl, immutable MonoTimeImpl))
{
foreach(U; _TypeTuple!(Duration, const Duration, immutable Duration))
{
T t = MonoTime.currTime;
T t = MonoTimeImpl.currTime;

// We reassign ticks in order to get the same rounding errors
// that we should be getting with Duration (e.g. MonoTime may be
// that we should be getting with Duration (e.g. MonoTimeImpl may be
// at a higher precision than hnsecs, meaning that 7333 would be
// truncated when converting to hnsecs).
long ticks = 7333;
auto hnsecs = convClockFreq(ticks, MonoTime.ticksPerSecond, hnsecsPer!"seconds");
ticks = convClockFreq(hnsecs, hnsecsPer!"seconds", MonoTime.ticksPerSecond);
auto hnsecs = convClockFreq(ticks, ticksPerSecond, hnsecsPer!"seconds");
ticks = convClockFreq(hnsecs, hnsecsPer!"seconds", ticksPerSecond);

assert(t - Duration(hnsecs) == MonoTime(t._ticks - ticks));
assert(t + Duration(hnsecs) == MonoTime(t._ticks + ticks));
assert(t - Duration(hnsecs) == MonoTimeImpl(t._ticks - ticks));
assert(t + Duration(hnsecs) == MonoTimeImpl(t._ticks + ticks));
}
}
}


/++ Ditto +/
ref MonoTime opOpAssign(string op)(Duration rhs) pure nothrow @nogc
ref MonoTimeImpl opOpAssign(string op)(Duration rhs) pure nothrow @nogc
if(op == "+" || op == "-")
{
immutable rhsConverted = convClockFreq(rhs._hnsecs, hnsecsPer!"seconds", ticksPerSecond);
Expand All @@ -2178,30 +2481,30 @@ assert(before + timeElapsed == after).

unittest
{
foreach(T; _TypeTuple!(const MonoTime, immutable MonoTime))
foreach(T; _TypeTuple!(const MonoTimeImpl, immutable MonoTimeImpl))
{
T t = MonoTime.currTime;
T t = MonoTimeImpl.currTime;
static assert(!is(typeof(t += Duration.zero)));
static assert(!is(typeof(t -= Duration.zero)));
}

foreach(T; _TypeTuple!(Duration, const Duration, immutable Duration))
{
auto mt = MonoTime.currTime;
auto mt = MonoTimeImpl.currTime;
auto initial = mt;
mt += T(0);
assert(mt == initial);
mt -= T(0);
assert(mt == initial);

// We reassign ticks in order to get the same rounding errors
// that we should be getting with Duration (e.g. MonoTime may be
// that we should be getting with Duration (e.g. MonoTimeImpl may be
// at a higher precision than hnsecs, meaning that 7333 would be
// truncated when converting to hnsecs).
long ticks = 7333;
auto hnsecs = convClockFreq(ticks, MonoTime.ticksPerSecond, hnsecsPer!"seconds");
ticks = convClockFreq(hnsecs, hnsecsPer!"seconds", MonoTime.ticksPerSecond);
auto before = MonoTime(initial._ticks - ticks);
auto hnsecs = convClockFreq(ticks, ticksPerSecond, hnsecsPer!"seconds");
ticks = convClockFreq(hnsecs, hnsecsPer!"seconds", ticksPerSecond);
auto before = MonoTimeImpl(initial._ticks - ticks);

assert((mt -= Duration(hnsecs)) == before);
assert(mt == before);
Expand Down Expand Up @@ -2229,7 +2532,7 @@ assert(before + timeElapsed == after).

unittest
{
auto mt = MonoTime.currTime;
auto mt = MonoTimeImpl.currTime;
assert(mt.ticks == mt._ticks);
}

Expand All @@ -2243,19 +2546,26 @@ assert(before + timeElapsed == after).
+/
static @property long ticksPerSecond() pure nothrow @nogc
{
return _ticksPerSecond;
return _ticksPerSecond[_clockIdx];
}

unittest
{
assert(MonoTime.ticksPerSecond == MonoTime._ticksPerSecond);
assert(MonoTimeImpl.ticksPerSecond == _ticksPerSecond[_clockIdx]);
}


///
string toString() const pure nothrow
{
return "MonoTime(" ~ numToString(_ticks) ~ " ticks, " ~ numToString(_ticksPerSecond) ~ " ticks per second)";
static if(clockType == ClockType.normal)
return "MonoTime(" ~ numToString(_ticks) ~ " ticks, " ~ numToString(ticksPerSecond) ~ " ticks per second)";
else
{
enum name = __traits(allMembers, ClockType)[_clockIdx];
return "MonoTimeImpl!(ClockType." ~ name ~ ")(" ~ numToString(_ticks) ~ " ticks, " ~
numToString(ticksPerSecond) ~ " ticks per second)";
}
}

unittest
Expand All @@ -2270,10 +2580,38 @@ assert(before + timeElapsed == after).
throw new AssertError("unittest failure", __FILE__, line);
}

immutable mt = MonoTime.currTime;
immutable mt = MonoTimeImpl.currTime;
auto str = mt.toString();
assert(str[0 .. "MonoTime(".length] == "MonoTime(");
str = str["MonoTime(".length .. $];
static if(is(typeof(this) == MonoTime))
{
assert(str[0 .. "MonoTime(".length] == "MonoTime(");
str = str["MonoTime(".length .. $];
}
else
{
enum len1 = "MonoTimeImpl!(ClockType.".length;
assert(str[0 .. len1] == "MonoTimeImpl!(ClockType.");

string name;
final switch(clockType)
{
foreach(val; __traits(allMembers, ClockType))
{
mixin("case ClockType." ~ val ~ ":");
{
name = val;
break;
}
}
}
assert(name.length > 0);

auto len2 = len1 + name.length;
assert(str[len1 .. len2] == name);
assert(str[len2 .. len2 + 2] == ")(");
str = str[len2 + 2 .. $];
}

immutable space1 = findSpace(str);
immutable ticksStr = str[0 .. space1];
assert(ticksStr == numToString(mt._ticks));
Expand All @@ -2282,52 +2620,155 @@ assert(before + timeElapsed == after).
str = str["ticks, ".length .. $];
immutable space2 = findSpace(str);
immutable ticksPerSecondStr = str[0 .. space2];
assert(ticksPerSecondStr == numToString(MonoTime.ticksPerSecond));
assert(ticksPerSecondStr == numToString(MonoTimeImpl.ticksPerSecond));
str = str[space2 + 1 .. $];
assert(str == "ticks per second)");
}

private:

static immutable long _ticksPerSecond;
// static immutable long _ticksPerSecond;

@trusted shared static this()
unittest
{
version(Windows)
assert(_ticksPerSecond[_clockIdx]);
}


long _ticks;
}

// This is supposed to be a static variable in MonoTimeImpl with the static
// constructor being in there, but https://issues.dlang.org/show_bug.cgi?id=14517
// prevents that from working. This is the workaround that Steven Schveighoffer
// came up with. Once issue# 14517 has been fixed, this should be changed so
// that _ticksPerSecond is a static long in MonoTimeImple rather than a static
// array outside of it.
private immutable long[__traits(allMembers, ClockType).length] _ticksPerSecond;

@trusted shared static this()
{
version(Windows)
{
long ticksPerSecond;
if(QueryPerformanceFrequency(&ticksPerSecond) != 0)
{
long ticksPerSecond;
if(QueryPerformanceFrequency(&ticksPerSecond) != 0)
_ticksPerSecond = ticksPerSecond;
foreach(i, typeStr; __traits(allMembers, ClockType))
_ticksPerSecond[i] = ticksPerSecond;
}
else version(OSX)
else
assert(0, "Failed to get the frequency of the system's monotonic clock.");
}
else version(OSX)
{
mach_timebase_info_data_t info;
if(mach_timebase_info(&info) == 0)
{
mach_timebase_info_data_t info;
if(mach_timebase_info(&info) == 0)
_ticksPerSecond = (1_000_000_000L * info.denom) / info.numer;
long ticksPerSecond = 1_000_000_000L * info.numer / info.denom;
foreach(i, typeStr; __traits(allMembers, ClockType))
_ticksPerSecond[i] = ticksPerSecond;
}
else version(Posix)
else
assert(0, "Failed to get the frequency of the system's monotonic clock.");
}
else version(Posix)
{
timespec ts;
foreach(i, typeStr; __traits(allMembers, ClockType))
{
timespec ts;
if(clock_getres(CLOCK_MONOTONIC, &ts) == 0)
mixin("enum clockType = ClockType." ~ typeStr ~ ";");
static if(clockType != ClockType.second)
{
// For some reason, on some systems, clock_getres returns
// a resolution which is clearly wrong (it's a millisecond
// or worse, but the time is updated much more frequently
// than that). In such cases, we'll just use nanosecond
// resolution.
_ticksPerSecond = ts.tv_nsec >= 1000 ? 1_000_000_000L
: 1_000_000_000L / ts.tv_nsec;
// This needs to be kept in sync with the corresponding code at the
// top of MonoTimeImpl, since as long as we're forced to put the
// static constructor outside of MonoTimeImpl, we don't have access
// to clockArg.
version(linux)
{
import core.sys.linux.time;
static if(clockType == ClockType.bootTime) alias clockArg = CLOCK_BOOTTIME;
else static if(clockType == ClockType.coarse) alias clockArg = CLOCK_MONOTONIC_COARSE;
else static if(clockType == ClockType.normal) alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.precise) alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.processCPUTime) alias clockArg = CLOCK_PROCESS_CPUTIME_ID;
else static if(clockType == ClockType.raw) alias clockArg = CLOCK_MONOTONIC_RAW;
else static if(clockType == ClockType.threadCPUTime) alias clockArg = CLOCK_THREAD_CPUTIME_ID;
else static assert(0, "ClockType." ~ typeStr ~ " is not supported by MonoTimeImpl on this system.");
}
else version(FreeBSD)
{
import core.sys.freebsd.time;
static if(clockType == ClockType.coarse) alias clockArg = CLOCK_MONOTONIC_FAST;
else static if(clockType == ClockType.normal) alias clockArg = CLOCK_MONOTONIC;
else static if(clockType == ClockType.precise) alias clockArg = CLOCK_MONOTONIC_PRECISE;
else static if(clockType == ClockType.uptime) alias clockArg = CLOCK_UPTIME;
else static if(clockType == ClockType.uptimeCoarse) alias clockArg = CLOCK_UPTIME_FAST;
else static if(clockType == ClockType.uptimePrecise) alias clockArg = CLOCK_UPTIME_PRECISE;
else static assert(0, "ClockType." ~ typeStr ~ " is not supported by MonoTimeImpl on this system.");
}
else
static assert(0, "What are the monotonic clock types supported by this system?");

if(clock_getres(clockArg, &ts) == 0)
{
// For some reason, on some systems, clock_getres returns
// a resolution which is clearly wrong (it's a millisecond
// or worse, but the time is updated much more frequently
// than that). In such cases, we'll just use nanosecond
// resolution.
_ticksPerSecond[i] = ts.tv_nsec >= 1000 ? 1_000_000_000L
: 1_000_000_000L / ts.tv_nsec;
}
else
assert(0, "Failed to get the frequency for of the system's monotonic clock: ClockType." ~ typeStr);
}
}
}
}

unittest
{
assert(_ticksPerSecond);
}

// Tests for MonoTimeImpl.currTime. It has to be outside, because MonoTimeImpl
// is a template. This unittest block also makes sure that MonoTimeImpl actually
// is instantiated with all of the various ClockTypes so that those types and
// their tests are compiled and run.
unittest
{
// This test is separate so that it can be tested with MonoTime and not just
// MonoTimeImpl.
auto norm1 = MonoTime.currTime;
auto norm2 = MonoTimeImpl!(ClockType.normal).currTime;
assert(norm1 <= norm2);

long _ticks;
foreach(typeStr; __traits(allMembers, ClockType))
{
// ClockType.second is currently the only clock type that doesn't work
// with MonoTimeImpl.
static if(typeStr != "second")
{
mixin("alias type = ClockType." ~ typeStr ~ ";");
auto v1 = MonoTimeImpl!type.currTime;
auto v2 = MonoTimeImpl!type.currTime;
scope(failure)
{
printf("%s: v1 %s, v2 %s, tps %s\n",
(type.stringof ~ "\0").ptr,
numToStringz(v1._ticks),
numToStringz(v2._ticks),
numToStringz(typeof(v1).ticksPerSecond));
}
assert(v1 <= v2);

foreach(otherStr; __traits(allMembers, ClockType))
{
static if(otherStr != "second")
{
mixin("alias other = ClockType." ~ otherStr ~ ";");
static assert(is(typeof({auto o1 = MonTimeImpl!other.currTime; auto b = v1 <= o1;})) ==
is(type == other));
}
}
}
}
}


Expand Down Expand Up @@ -4282,18 +4723,18 @@ unittest
/+
Local version of abs, since std.math.abs is in Phobos, not druntime.
+/
private long _abs(long val) @safe pure nothrow @nogc
long _abs(long val) @safe pure nothrow @nogc
{
return val >= 0 ? val : -val;
}

private double _abs(double val) @safe pure nothrow @nogc
double _abs(double val) @safe pure nothrow @nogc
{
return val >= 0.0 ? val : -val;
}


/++
/+
Unfortunately, $(D snprintf) is not pure, so here's a way to convert
a number to a string which is.
+/
Expand Down Expand Up @@ -4329,14 +4770,14 @@ string numToString(long value) @safe pure nothrow
assert(0, "Something threw when nothing can throw.");
}

version(unittest) const(char)* numToStringz(long value) @safe pure nothrow
version(unittest) const(char)* numToStringz()(long value) @safe pure nothrow
{
return (numToString(value) ~ "\0").ptr;
}


/+ A copy of std.typecons.TypeTuple. +/
private template _TypeTuple(TList...)
template _TypeTuple(TList...)
{
alias TList _TypeTuple;
}
Expand Down Expand Up @@ -4466,20 +4907,21 @@ version(unittest) void assertApprox(D, E)(D actual,
}
}

version(unittest) void assertApprox(MonoTime actual,
MonoTime lower,
MonoTime upper,
string msg = "unittest failure",
size_t line = __LINE__)
version(unittest) void assertApprox(MT)(MT actual,
MT lower,
MT upper,
string msg = "unittest failure",
size_t line = __LINE__)
if(is(MT == MonoTimeImpl!type, ClockType type))
{
assertApprox(actual._ticks, lower._ticks, upper._ticks, msg, line);
}

version(unittest) void assertApprox(long actual,
long lower,
long upper,
string msg = "unittest failure",
size_t line = __LINE__)
version(unittest) void assertApprox()(long actual,
long lower,
long upper,
string msg = "unittest failure",
size_t line = __LINE__)
{
if(actual < lower)
throw new AssertError(msg ~ ": lower: " ~ numToString(actual), __FILE__, line);
Expand Down