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