blob: 7f3b0cf3f652f1999e8556dacb82b83ed099403b [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "wpi/timestamp.h"
9
10#include <atomic>
11
12#ifdef _WIN32
13#include <windows.h>
14
15#include <cassert>
16#include <exception>
17#else
18#include <chrono>
19#endif
20
21// offset in microseconds
22static uint64_t zerotime() {
23#ifdef _WIN32
24 FILETIME ft;
25 uint64_t tmpres = 0;
26 // 100-nanosecond intervals since January 1, 1601 (UTC)
27 // which means 0.1 us
28 GetSystemTimeAsFileTime(&ft);
29 tmpres |= ft.dwHighDateTime;
30 tmpres <<= 32;
31 tmpres |= ft.dwLowDateTime;
32 tmpres /= 10u; // convert to us
33 // January 1st, 1970 - January 1st, 1601 UTC ~ 369 years
34 // or 11644473600000000 us
35 static const uint64_t deltaepoch = 11644473600000000ull;
36 tmpres -= deltaepoch;
37 return tmpres;
38#else
39 // 1-us intervals
40 return std::chrono::duration_cast<std::chrono::microseconds>(
41 std::chrono::high_resolution_clock::now().time_since_epoch())
42 .count();
43#endif
44}
45
46static uint64_t timestamp() {
47#ifdef _WIN32
48 LARGE_INTEGER li;
49 QueryPerformanceCounter(&li);
50 // there is an imprecision with the initial value,
51 // but what matters is that timestamps are monotonic and consistent
52 return static_cast<uint64_t>(li.QuadPart);
53#else
54 // 1-us intervals
55 return std::chrono::duration_cast<std::chrono::microseconds>(
56 std::chrono::steady_clock::now().time_since_epoch())
57 .count();
58#endif
59}
60
61#ifdef _WIN32
62static uint64_t update_frequency() {
63 LARGE_INTEGER li;
64 if (!QueryPerformanceFrequency(&li) || !li.QuadPart) {
65 // log something
66 std::terminate();
67 }
68 return static_cast<uint64_t>(li.QuadPart);
69}
70#endif
71
72static const uint64_t zerotime_val = zerotime();
73static const uint64_t offset_val = timestamp();
74#ifdef _WIN32
75static const uint64_t frequency_val = update_frequency();
76#endif
77
78uint64_t wpi::NowDefault() {
79#ifdef _WIN32
80 assert(offset_val > 0u);
81 assert(frequency_val > 0u);
82 uint64_t delta = timestamp() - offset_val;
83 // because the frequency is in update per seconds, we have to multiply the
84 // delta by 1,000,000
85 uint64_t delta_in_us = delta * 1000000ull / frequency_val;
86 return delta_in_us + zerotime_val;
87#else
88 return zerotime_val + timestamp() - offset_val;
89#endif
90}
91
92static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
93
94void wpi::SetNowImpl(uint64_t (*func)(void)) {
95 now_impl = func ? func : NowDefault;
96}
97
98uint64_t wpi::Now() { return (now_impl.load())(); }
99
100extern "C" {
101
102uint64_t WPI_NowDefault(void) { return wpi::NowDefault(); }
103
104void WPI_SetNowImpl(uint64_t (*func)(void)) { wpi::SetNowImpl(func); }
105
106uint64_t WPI_Now(void) { return wpi::Now(); }
107
108} // extern "C"