Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
36b91bdb
Commit
36b91bdb
authored
Jun 22, 2019
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add basic system clock APIs.
parent
82d2ec62
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
399 additions
and
2 deletions
+399
-2
Makefile.am
c++/Makefile.am
+1
-0
CMakeLists.txt
c++/src/kj/CMakeLists.txt
+1
-0
time-test.c++
c++/src/kj/time-test.c++
+135
-0
time.c++
c++/src/kj/time.c++
+221
-0
time.h
c++/src/kj/time.h
+41
-2
No files found.
c++/Makefile.am
View file @
36b91bdb
...
...
@@ -522,6 +522,7 @@ capnp_test_SOURCES = \
src/kj/function-test.c++
\
src/kj/io-test.c++
\
src/kj/mutex-test.c++
\
src/kj/time-test.c++
\
src/kj/threadlocal-test.c++
\
src/kj/threadlocal-pthread-test.c++
\
src/kj/filesystem-test.c++
\
...
...
c++/src/kj/CMakeLists.txt
View file @
36b91bdb
...
...
@@ -202,6 +202,7 @@ if(BUILD_TESTING)
debug-test.c++
io-test.c++
mutex-test.c++
time-test.c++
threadlocal-test.c++
test-test.c++
std/iostream-test.c++
...
...
c++/src/kj/time-test.c++
0 → 100644
View file @
36b91bdb
// Copyright (c) 2019 Cloudflare, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1 // lolz
#endif
#include "time.h"
#include "debug.h"
#include <kj/test.h>
#include <time.h>
#if _WIN32
#include <windows.h>
#include "windows-sanity.h"
#else
#include <unistd.h>
#endif
namespace
kj
{
namespace
{
#if _WIN32
void
delay
(
kj
::
Duration
d
)
{
Sleep
(
d
/
kj
::
MILLISECONDS
);
}
#else
void
delay
(
kj
::
Duration
d
)
{
usleep
(
d
/
kj
::
MICROSECONDS
);
}
#endif
KJ_TEST
(
"calendar clocks matches unix time"
)
{
// Check that the times returned by the calendar clock are within 1s of what time() returns.
auto
&
coarse
=
systemCoarseCalendarClock
();
auto
&
precise
=
systemPreciseCalendarClock
();
Date
p
=
precise
.
now
();
Date
c
=
coarse
.
now
();
time_t
t
=
time
(
nullptr
);
int64_t
pi
=
(
p
-
UNIX_EPOCH
)
/
kj
::
SECONDS
;
int64_t
ci
=
(
c
-
UNIX_EPOCH
)
/
kj
::
SECONDS
;
KJ_EXPECT
(
pi
>=
t
-
1
);
KJ_EXPECT
(
pi
<=
t
+
1
);
KJ_EXPECT
(
ci
>=
t
-
1
);
KJ_EXPECT
(
ci
<=
t
+
1
);
}
KJ_TEST
(
"monotonic clocks match each other"
)
{
// Check that the monotonic clocks return comparable times.
auto
&
coarse
=
systemCoarseMonotonicClock
();
auto
&
precise
=
systemPreciseMonotonicClock
();
TimePoint
p
=
precise
.
now
();
TimePoint
c
=
coarse
.
now
();
// 20ms tolerance due to Windows timeslices being quite long.
KJ_EXPECT
(
p
<
c
+
20
*
kj
::
MILLISECONDS
);
KJ_EXPECT
(
p
>
c
-
20
*
kj
::
MILLISECONDS
);
}
KJ_TEST
(
"all clocks advance in real time"
)
{
Duration
coarseCalDiff
;
Duration
preciseCalDiff
;
Duration
coarseMonoDiff
;
Duration
preciseMonoDiff
;
for
(
uint
retryCount
KJ_UNUSED
:
kj
::
zeroTo
(
20
))
{
auto
&
coarseCal
=
systemCoarseCalendarClock
();
auto
&
preciseCal
=
systemPreciseCalendarClock
();
auto
&
coarseMono
=
systemCoarseMonotonicClock
();
auto
&
preciseMono
=
systemPreciseMonotonicClock
();
Date
coarseCalBefore
=
coarseCal
.
now
();
Date
preciseCalBefore
=
preciseCal
.
now
();
TimePoint
coarseMonoBefore
=
coarseMono
.
now
();
TimePoint
preciseMonoBefore
=
preciseMono
.
now
();
Duration
delayTime
=
150
*
kj
::
MILLISECONDS
;
delay
(
delayTime
);
Date
coarseCalAfter
=
coarseCal
.
now
();
Date
preciseCalAfter
=
preciseCal
.
now
();
TimePoint
coarseMonoAfter
=
coarseMono
.
now
();
TimePoint
preciseMonoAfter
=
preciseMono
.
now
();
coarseCalDiff
=
coarseCalAfter
-
coarseCalBefore
;
preciseCalDiff
=
preciseCalAfter
-
preciseCalBefore
;
coarseMonoDiff
=
coarseMonoAfter
-
coarseMonoBefore
;
preciseMonoDiff
=
preciseMonoAfter
-
preciseMonoBefore
;
// 20ms tolerance due to Windows timeslices being quite long (and Windows sleeps being only
// accurate to the timeslice).
if
(
coarseCalDiff
>
delayTime
-
20
*
kj
::
MILLISECONDS
&&
coarseCalDiff
<
delayTime
+
20
*
kj
::
MILLISECONDS
&&
preciseCalDiff
>
delayTime
-
20
*
kj
::
MILLISECONDS
&&
preciseCalDiff
<
delayTime
+
20
*
kj
::
MILLISECONDS
&&
coarseMonoDiff
>
delayTime
-
20
*
kj
::
MILLISECONDS
&&
coarseMonoDiff
<
delayTime
+
20
*
kj
::
MILLISECONDS
&&
preciseMonoDiff
>
delayTime
-
20
*
kj
::
MILLISECONDS
&&
preciseMonoDiff
<
delayTime
+
20
*
kj
::
MILLISECONDS
)
{
// success
return
;
}
}
KJ_FAIL_EXPECT
(
"clocks seem inaccurate even after 20 tries"
,
coarseCalDiff
/
kj
::
MICROSECONDS
,
preciseCalDiff
/
kj
::
MICROSECONDS
,
coarseMonoDiff
/
kj
::
MICROSECONDS
,
preciseMonoDiff
/
kj
::
MICROSECONDS
);
}
}
// namespace
}
// namespace kj
c++/src/kj/time.c++
View file @
36b91bdb
...
...
@@ -20,10 +20,22 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if _WIN32
#define WIN32_LEAN_AND_MEAN 1 // lolz
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#endif
#include "time.h"
#include "debug.h"
#include <set>
#if _WIN32
#include <windows.h>
#else
#include <time.h>
#endif
namespace
kj
{
const
Clock
&
nullClock
()
{
...
...
@@ -35,4 +47,213 @@ const Clock& nullClock() {
return
NULL_CLOCK
;
}
#if _WIN32
namespace
{
static
constexpr
int64_t
WIN32_EPOCH_OFFSET
=
116444736000000000ull
;
// Number of 100ns intervals from Jan 1, 1601 to Jan 1, 1970.
static
Date
toKjDate
(
FILETIME
t
)
{
int64_t
value
=
(
static_cast
<
uint64_t
>
(
t
.
dwHighDateTime
)
<<
32
)
|
t
.
dwLowDateTime
;
return
(
value
-
WIN32_EPOCH_OFFSET
)
*
(
100
*
kj
::
NANOSECONDS
)
+
UNIX_EPOCH
;
}
class
Win32CoarseClock
:
public
Clock
{
public
:
Date
now
()
const
override
{
FILETIME
ft
;
GetSystemTimeAsFileTime
(
&
ft
);
return
toKjDate
(
ft
);
}
};
class
Win32PreciseClock
:
public
Clock
{
typedef
VOID
WINAPI
GetSystemTimePreciseAsFileTimeFunc
(
LPFILETIME
);
public
:
Date
now
()
const
override
{
static
const
GetSystemTimePreciseAsFileTimeFunc
*
getSystemTimePreciseAsFileTimePtr
=
getGetSystemTimePreciseAsFileTime
();
FILETIME
ft
;
if
(
getSystemTimePreciseAsFileTimePtr
==
nullptr
)
{
// We can't use QueryPerformanceCounter() to get any more precision because we have no way
// of knowing when the calendar clock jumps. So I guess we're stuck.
GetSystemTimeAsFileTime
(
&
ft
);
}
else
{
getSystemTimePreciseAsFileTimePtr
(
&
ft
);
}
return
toKjDate
(
ft
);
}
private
:
static
GetSystemTimePreciseAsFileTimeFunc
*
getGetSystemTimePreciseAsFileTime
()
{
// Dynamically look up the function GetSystemTimePreciseAsFileTimeFunc(). This was only
// introduced as of Windows 8, so it might be missing.
return
reinterpret_cast
<
GetSystemTimePreciseAsFileTimeFunc
*>
(
GetProcAddress
(
GetModuleHandleA
(
"kernel32.dll"
),
"GetSystemTimePreciseAsFileTime"
));
}
};
class
Win32CoarseMonotonicClock
:
public
MonotonicClock
{
public
:
TimePoint
now
()
const
override
{
return
kj
::
origin
<
TimePoint
>
()
+
GetTickCount64
()
*
kj
::
MILLISECONDS
;
}
};
class
Win32PreciseMonotonicClock
:
public
MonotonicClock
{
// Precise clock implemented using QueryPerformanceCounter().
//
// TODO(someday): Windows 10 has QueryUnbiasedInterruptTime() and
// QueryUnbiasedInterruptTimePrecise(), a new API for monotonic timing that isn't as difficult.
// Is there any benefit to dynamically checking for these and using them if available?
public
:
TimePoint
now
()
const
override
{
static
const
QpcProperties
props
;
LARGE_INTEGER
now
;
QueryPerformanceCounter
(
&
now
);
uint64_t
adjusted
=
now
.
QuadPart
-
props
.
origin
;
uint64_t
ns
=
mulDiv64
(
adjusted
,
1'000'000'000
,
props
.
frequency
);
return
kj
::
origin
<
TimePoint
>
()
+
ns
*
kj
::
NANOSECONDS
;
}
private
:
struct
QpcProperties
{
uint64_t
origin
;
// What QueryPerformanceCounter() would have returned at the time when GetTickCount64() returned
// zero. Used to ensure that the coarse and precise timers return similar values.
uint64_t
frequency
;
// From QueryPerformanceFrequency().
QpcProperties
()
{
LARGE_INTEGER
now
,
freqLi
;
uint64_t
ticks
=
GetTickCount64
();
QueryPerformanceCounter
(
&
now
);
QueryPerformanceFrequency
(
&
freqLi
);
frequency
=
freqLi
.
QuadPart
;
// Convert the millisecond tick count into performance counter ticks.
uint64_t
ticksAsQpc
=
mulDiv64
(
ticks
,
freqLi
.
QuadPart
,
1000
);
origin
=
now
.
QuadPart
-
ticksAsQpc
;
}
};
static
inline
uint64_t
mulDiv64
(
uint64_t
value
,
uint64_t
numer
,
uint64_t
denom
)
{
// Inspired by:
// https://github.com/rust-lang/rust/pull/22788/files#diff-24f054cd23f65af3b574c6ce8aa5a837R54
// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into 64 bits.
uint64_t
q
=
value
/
denom
;
uint64_t
r
=
value
%
denom
;
return
q
*
numer
+
r
*
numer
/
denom
;
}
};
}
// namespace
const
Clock
&
systemCoarseCalendarClock
()
{
static
constexpr
Win32CoarseClock
clock
;
return
clock
;
}
const
Clock
&
systemPreciseCalendarClock
()
{
static
constexpr
Win32PreciseClock
clock
;
return
clock
;
}
const
MonotonicClock
&
systemCoarseMonotonicClock
()
{
static
constexpr
Win32CoarseMonotonicClock
clock
;
return
clock
;
}
const
MonotonicClock
&
systemPreciseMonotonicClock
()
{
static
constexpr
Win32PreciseMonotonicClock
clock
;
return
clock
;
}
#else
namespace
{
class
PosixClock
:
public
Clock
{
public
:
constexpr
PosixClock
(
clockid_t
clockId
)
:
clockId
(
clockId
)
{}
Date
now
()
const
override
{
struct
timespec
ts
;
KJ_SYSCALL
(
clock_gettime
(
clockId
,
&
ts
));
return
UNIX_EPOCH
+
ts
.
tv_sec
*
kj
::
SECONDS
+
ts
.
tv_nsec
*
kj
::
NANOSECONDS
;
}
private
:
clockid_t
clockId
;
};
class
PosixMonotonicClock
:
public
MonotonicClock
{
public
:
constexpr
PosixMonotonicClock
(
clockid_t
clockId
)
:
clockId
(
clockId
)
{}
TimePoint
now
()
const
override
{
struct
timespec
ts
;
KJ_SYSCALL
(
clock_gettime
(
clockId
,
&
ts
));
return
kj
::
origin
<
TimePoint
>
()
+
ts
.
tv_sec
*
kj
::
SECONDS
+
ts
.
tv_nsec
*
kj
::
NANOSECONDS
;
}
private
:
clockid_t
clockId
;
};
}
// namespace
// FreeBSD has "_PRECISE", but Linux just defaults to precise.
#ifndef CLOCK_REALTIME_PRECISE
#define CLOCK_REALTIME_PRECISE CLOCK_REALTIME
#endif
#ifndef CLOCK_MONOTONIC_PRECISE
#define CLOCK_MONOTONIC_PRECISE CLOCK_MONOTONIC
#endif
// FreeBSD has "_FAST", Linux has "_COARSE".
// MacOS has an "_APPROX" but only for CLOCK_MONOTONIC_RAW, which isn't helpful.
#ifndef CLOCK_REALTIME_COARSE
#ifdef CLOCK_REALTIME_FAST
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME_FAST
#else
#define CLOCK_REALTIME_COARSE CLOCK_REALTIME
#endif
#endif
#ifndef CLOCK_MONOTONIC_COARSE
#ifdef CLOCK_MONOTONIC_FAST
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_FAST
#else
#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC
#endif
#endif
const
Clock
&
systemCoarseCalendarClock
()
{
static
constexpr
PosixClock
clock
(
CLOCK_REALTIME_COARSE
);
return
clock
;
}
const
Clock
&
systemPreciseCalendarClock
()
{
static
constexpr
PosixClock
clock
(
CLOCK_REALTIME_PRECISE
);
return
clock
;
}
const
MonotonicClock
&
systemCoarseMonotonicClock
()
{
static
constexpr
PosixMonotonicClock
clock
(
CLOCK_MONOTONIC_COARSE
);
return
clock
;
}
const
MonotonicClock
&
systemPreciseMonotonicClock
()
{
static
constexpr
PosixMonotonicClock
clock
(
CLOCK_MONOTONIC_PRECISE
);
return
clock
;
}
#endif
}
// namespace kj
c++/src/kj/time.h
View file @
36b91bdb
...
...
@@ -50,8 +50,9 @@ constexpr Duration HOURS = 60 * MINUTES;
constexpr
Duration
DAYS
=
24
*
HOURS
;
using
TimePoint
=
Absolute
<
Duration
,
_
::
TimeLabel
>
;
// An absolute time measured by some particular instance of `Timer`. `Time`s from two different
// `Timer`s may be measured from different origins and so are not necessarily compatible.
// An absolute time measured by some particular instance of `Timer` or `MonotonicClock`. `Time`s
// from two different `Timer`s or `MonotonicClock`s may be measured from different origins and so
// are not necessarily compatible.
using
Date
=
Absolute
<
Duration
,
_
::
DateLabel
>
;
// A point in real-world time, measured relative to the Unix epoch (Jan 1, 1970 00:00:00 UTC).
...
...
@@ -65,8 +66,46 @@ public:
virtual
Date
now
()
const
=
0
;
};
class
MonotonicClock
{
// Interface to read time in a way that increases as real-world time increases, independent of
// any manual changes to the calendar date/time. Such a clock never "goes backwards" even if the
// system administrator changes the calendar time or suspends the system. However, this clock's
// time points are only meaningful in comparison to other time points from the same clock, and
// cannot be used to determine the current calendar date.
public
:
virtual
TimePoint
now
()
const
=
0
;
};
const
Clock
&
nullClock
();
// A clock which always returns UNIX_EPOCH as the current time. Useful when you don't care about
// time.
const
Clock
&
systemCoarseCalendarClock
();
const
Clock
&
systemPreciseCalendarClock
();
// A clock that reads the real system time.
//
// In well-designed code, this should only be called by the top-level dependency injector. All
// other modules should request that the caller provide a Clock so that alternate clock
// implementations can be injected for testing, simulation, reproducibility, and other purposes.
//
// The "coarse" version has precision around 1-10ms, while the "precise" version has precision
// better than 1us. The "precise" version may be slightly slower, though on modern hardware and
// a reasonable operating system the difference is usually negligible.
//
// Note: On Windows prior to Windows 8, there is no precise calendar clock; the "precise" clock
// will be no more precise than the "coarse" clock in this case.
const
MonotonicClock
&
systemCoarseMonotonicClock
();
const
MonotonicClock
&
systemPreciseMonotonicClock
();
// A MonotonicClock that reads the real system time.
//
// In well-designed code, this should only be called by the top-level dependency injector. All
// other modules should request that the caller provide a Clock so that alternate clock
// implementations can be injected for testing, simulation, reproducibility, and other purposes.
//
// The "coarse" version has precision around 1-10ms, while the "precise" version has precision
// better than 1us. The "precise" version may be slightly slower, though on modern hardware and
// a reasonable operating system the difference is usually negligible.
}
// namespace kj
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment