8#ifndef PEDIGREE_KERNEL_MACHINE_MACH_PC_TSCCLOCK_H
9#define PEDIGREE_KERNEL_MACHINE_MACH_PC_TSCCLOCK_H
10#include "pedigree/kernel/compiler.h"
11#include "pedigree/kernel/processor/types.h"
15static_assert(__atomic_always_lock_free(
sizeof(uint64_t),
nullptr),
16 "the PC monotonic clock requires lock-free 64-bit publication");
19constexpr uint64_t MaximumTimestamp = ~static_cast<uint64_t>(0);
22 Calibration(uint64_t cycles = 1, uint64_t nanoseconds = 1)
23 : cycles(cycles), nanoseconds(nanoseconds), whole(0), multiplier(0), fractional(0) {
28 whole = nanoseconds / cycles;
29 fractional = nanoseconds % cycles;
34#if defined(__x86_64__)
39 :
"=a"(multiplier),
"=d"(remainder)
40 :
"0"(uint64_t(0)),
"1"(fractional),
"r"(cycles)
43 multiplier =
static_cast<uint64_t
>((
static_cast<unsigned __int128
>(fractional) << 64) / cycles);
55 const unsigned __int128 product =
static_cast<unsigned __int128
>(cycles) * calibration.multiplier;
56 uint64_t nanoseconds =
static_cast<uint64_t
>(product >> 64);
57 const uint64_t fraction =
static_cast<uint64_t
>(product);
63 if (fraction > MaximumTimestamp - cycles) {
65 if (
static_cast<unsigned __int128
>(cycles) * calibration.fractional >=
66 static_cast<unsigned __int128
>(nanoseconds + 1) * calibration.cycles) {
71 if (!calibration.whole) {
75 const unsigned __int128 result =
76 static_cast<unsigned __int128
>(cycles) * calibration.whole + nanoseconds;
77 return result > MaximumTimestamp ? MaximumTimestamp :
static_cast<uint64_t
>(result);
80ALWAYS_INLINE inline uint64_t saturatingAdd(uint64_t first, uint64_t second) {
81 return first > (MaximumTimestamp - second) ? MaximumTimestamp : first + second;
84ALWAYS_INLINE inline uint64_t fromAnchor(uint64_t currentTsc, uint64_t anchorTsc,
85 uint64_t anchorNanoseconds,
86 const Calibration& calibration) {
87 if (currentTsc < anchorTsc) {
88 return anchorNanoseconds;
91 return saturatingAdd(anchorNanoseconds, scale(currentTsc - anchorTsc, calibration));
94constexpr uint64_t publicationResult(uint64_t candidate, uint64_t observed,
bool exchanged) {
95 return exchanged ? candidate : observed;
109 uint64_t publish(uint64_t candidate) {
110 uint64_t observed = __atomic_load_n(&m_Value, __ATOMIC_ACQUIRE);
111 if (candidate <= observed) {
115 const bool exchanged = __atomic_compare_exchange_n(&m_Value, &observed, candidate,
false,
116 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
118 return publicationResult(candidate, observed, exchanged);
121 uint64_t value()
const {
122 return __atomic_load_n(&m_Value, __ATOMIC_ACQUIRE);
125 void reset(uint64_t value = 0) {
126 __atomic_store_n(&m_Value, value, __ATOMIC_RELEASE);