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
5 years ago
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add basic system clock APIs.
parent
82d2ec62
Hide 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++
\
...
...
This diff is collapsed.
Click to expand it.
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++
...
...
This diff is collapsed.
Click to expand it.
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
This diff is collapsed.
Click to expand it.
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
This diff is collapsed.
Click to expand it.
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
This diff is collapsed.
Click to expand it.
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