AuroraRuntime/Source/Time/AuClock.cpp
Jamie Reece Wilson fc08e8351d [+] Begin work on a POSIX 2001 based clock implementation as opposed to the retarded stinking free-shit glibc toddlers hacked together alongside linux vdso. Once again, a POSIX spec from 2001 and NT kernel releases from 2 decades ago are much better than whatever the hell Linux toddlers are up to in the 2020s.
Fuck your 1998 POSIX1.b UNIX ass interface with extensions that dont even provide the functionality of POSIX 2001.

Much like how UWP is forcing us to support Windows XP, Linux retardation is pushing us towards legacy POSIX support (the deprecation of AuProcesses pidfd requirements, now this)

Alongside this, #60, this is yet another monthly reminder that the Linux kernel maintainers are the most useless incompetent boomers gluing forced-source drivers into a monolithic mess.

PS: if i wasnt malding before, i am now after trying to unearth the context of this disaster.

https://nextcloud.reece.sx/index.php/s/jx82SsygJYWSir6/preview
https://nextcloud.reece.sx/index.php/s/RpRpNbnBixyMT5G/preview
https://nextcloud.reece.sx/index.php/s/NfMfDCcxL6sfXCf/preview
https://nextcloud.reece.sx/index.php/s/7A4t5JNzzFctPT6/preview
https://nextcloud.reece.sx/index.php/s/cyYwm8GPLKiLFqS/preview Bills peen company had this figured out before < LINUX 2.2 1999 EVEN HAD CLOCK APIs. ARE YOU FUCKING KIDDING? LINUXs MAIN CLOCK IMPLEMENTATION WASNT EVEN DONE UNTIL 2002 BY ONE GEORGE BECAUSE PATHER DIDNT FUCK IT UP ENOUGH?

Freetards should get the wall. I don't know how these braindead fuckers could've ever eaten a bowl of cereal without choking on the milk and dying.

* 2002-10-15  Posix Clocks & timers by George Anzinger
 [...]
 These are all the functions necessary to implement
 POSIX clocks & timers

Meanwhile at Microsoft in 1997, ...

I just cant anymore. Why the fuck are we even paying lip service to Linshit?
2023-08-04 00:42:51 +01:00

568 lines
24 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuClock.cpp
Date: 2021-6-13
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuClock.hpp"
#include "Time.hpp"
#if defined(AURORA_IS_MODERNNT_DERIVED)
// TODO (Reece): ....
// benchmarking:
// https://github.com/microsoft/STL/issues/2085
struct steady_clock_fast
{ // wraps QueryPerformanceCounter
using rep = long long;
using period = std::nano;
using duration = std::chrono::nanoseconds;
using time_point = _CHRONO time_point<steady_clock_fast>;
static constexpr bool is_steady = true;
_NODISCARD static time_point now() noexcept
{ // get current time
static const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot
const long long _Ctr = _Query_perf_counter();
static_assert(period::num == 1, "This assumes period::num == 1.");
// Instead of just having "(_Ctr * period::den) / _Freq",
// the algorithm below prevents overflow when _Ctr is sufficiently large.
// It assumes that _Freq * period::den does not overflow, which is currently true for nano period.
// It is not realistic for _Ctr to accumulate to large values from zero with this assumption,
// but the initial value of _Ctr could be large.
// 10 MHz is a very common QPC frequency on modern PCs. Optimizing for
// this specific frequency can double the performance of this function by
// avoiding the expensive frequency conversion path.
if (_Freq == 10000000)
{
return time_point(duration(_Ctr * 100));
}
else
{
const long long _Whole = (_Ctr / _Freq) * period::den;
const long long _Part = (_Ctr % _Freq) * period::den / _Freq;
return time_point(duration(_Whole + _Part));
}
}
};
// ~3.0741 seconds
using high_res_clock = steady_clock_fast;
// holy fuck, we're keeping this
// ~2x improvement
#else
// ~6.07 seconds
using high_res_clock = std::chrono::high_resolution_clock;
#endif
using sys_clock = std::chrono::system_clock;
using steady_clock = std::chrono::steady_clock;
#if defined(AURORA_PLATFORM_WIN32)
#define timegm _mkgmtime
#endif
static sys_clock::duration gEpoch;
static sys_clock::duration gUnixDelta;
static auto InitEpoch()
{
std::tm start{0, 15, 10, 29, 7, 101, 0, 0, 0};
auto epoch = sys_clock::from_time_t(timegm(&start)).time_since_epoch();
std::tm unixStart{};
unixStart.tm_mday = 1;
unixStart.tm_year = 70;
// dont care what the spec says, you can't trust some ms stls
// sys_clock can have its own epoch for all we care
auto nixEpoch = sys_clock::from_time_t(timegm(&unixStart)).time_since_epoch();
gUnixDelta = epoch - nixEpoch;
gEpoch = epoch;
return 0;
}
static auto ___ = InitEpoch();
template<typename T>
static inline T NormalizeEpoch(T sysEpoch)
{
return sysEpoch - gEpoch;
}
template<typename T>
static inline T DecodeEpoch(T auroraEpoch)
{
return auroraEpoch + gEpoch;
}
template<typename Clock_t, typename Duration_t>
static auto TimeFromDurationSinceEpoch(Duration_t in)
{
auto duration = std::chrono::duration_cast<typename Clock_t::duration>(in);
return std::chrono::time_point<Clock_t>(DecodeEpoch(duration));
}
template<typename Duration_t>
static time_t CalculateTimeT(AuUInt64 in)
{
return sys_clock::to_time_t(TimeFromDurationSinceEpoch<sys_clock>(Duration_t(in)));
}
namespace Aurora::Time
{
// removed from public header / deprecating
AUKN_SYM AuUInt64 HighResClock();
AUKN_SYM AuUInt64 HighResClockNS();
AUKN_SYM AuUInt64 HighResClockMS();
AUKN_SYM AuUInt64 HighResClockJiffies();
AUKN_SYM time_t SToCTime(AuInt64 time)
{
return CalculateTimeT<std::chrono::seconds>(time);
}
AUKN_SYM time_t NSToCTime(AuInt64 time)
{
return CalculateTimeT<std::chrono::nanoseconds>(time);
}
AUKN_SYM time_t MSToCTime(AuInt64 time)
{
return CalculateTimeT<std::chrono::milliseconds>(time);
}
AUKN_SYM AuInt64 CurrentClock()
{
return NormalizeEpoch(sys_clock::now().time_since_epoch()).count();
}
AUKN_SYM AuInt64 CurrentClockMS()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(NormalizeEpoch(sys_clock::now().time_since_epoch())).count();
}
AUKN_SYM AuInt64 CurrentClockNS()
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(NormalizeEpoch(sys_clock::now().time_since_epoch())).count();
}
AUKN_SYM AuUInt64 SteadyClock()
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
return _Query_perf_counter();
#endif
return SteadyClockNS() / (1000000000ull / SteadyClockJiffies());
}
AUKN_SYM AuUInt64 SteadyClockMS()
{
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_gettime(CLOCK_MONOTONIC, &spec) == 0)
{
return AuSToMS<AuUInt64>(spec.tv_sec) + AuNSToMS<AuUInt64>(spec.tv_nsec);
}
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
return std::chrono::duration_cast<std::chrono::milliseconds>(high_res_clock::now().time_since_epoch()).count();
#endif
return std::chrono::duration_cast<std::chrono::milliseconds>(steady_clock::now().time_since_epoch()).count();
}
AUKN_SYM AuUInt64 SteadyClockNS()
{
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_gettime(CLOCK_MONOTONIC, &spec) == 0)
{
return AuMSToNS<AuUInt64>(AuSToMS<AuUInt64>(spec.tv_sec)) + (AuUInt64)spec.tv_nsec;
}
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
return std::chrono::duration_cast<std::chrono::nanoseconds>(high_res_clock::now().time_since_epoch()).count();
#endif
return std::chrono::duration_cast<std::chrono::nanoseconds>(steady_clock::now().time_since_epoch()).count();
}
AUKN_SYM AuInt64 CTimeToMS(time_t time)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(NormalizeEpoch(sys_clock::from_time_t(time).time_since_epoch())).count();
}
AuInt64 CTimeNSNormalize(AuUInt64 time)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(NormalizeEpoch(std::chrono::nanoseconds(time))).count();
}
#if defined(AURORA_IS_POSIX_DERIVED)
enum class EPseudoPosixClock
{
eUser,
eKernel,
eAll
};
static AuUInt64 GetPOSIXTimeEx(struct rusage *usage, EPseudoPosixClock e)
{
struct timeval *tv {};
switch (e)
{
case EPseudoPosixClock::eAll:
{
return GetPOSIXTimeEx(usage, EPseudoPosixClock::eKernel) +
GetPOSIXTimeEx(usage, EPseudoPosixClock::eUser);
}
case EPseudoPosixClock::eUser:
{
tv = &usage->ru_utime;
break;
}
case EPseudoPosixClock::eKernel:
{
tv = &usage->ru_stime;
break;
}
};
auto uMS = AuSToMS<AuUInt64>(tv->tv_sec);
auto uNS = AuMSToNS<AuUInt64>(uMS) +
tv->tv_usec * 1'000ull;
return uNS;
}
static AuUInt64 GetPOSIXTime(bool bThread, EPseudoPosixClock e)
{
struct rusage usage;
getrusage(bThread ? RUSAGE_THREAD : RUSAGE_SELF,
&usage);
return GetPOSIXTimeEx(&usage, e);
}
#if !defined(CLOCK_THREAD_CPUTIME_ID)
#define CLOCK_THREAD_CPUTIME_ID 0
#endif
#if !defined(CLOCK_PROCESS_CPUTIME_ID)
#define CLOCK_PROCESS_CPUTIME_ID 0
#endif
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED)
#define ADD_CLOCK_FAMILY(fn, type, expr, posixId, posixCall) \
AUKN_SYM AuUInt64 fn ## ClockJiffies(); \
\
AUKN_SYM AuUInt64 fn ## ClockMS() \
{ \
return AuNSToMS<AuUInt64>(fn ## ClockNS()); \
} \
\
AUKN_SYM AuUInt64 fn ## ClockNS() \
{ \
FILETIME creation, exit, kernel, user; \
if (::Get ## type ## Times(GetCurrent ## type(), &creation, &exit, &kernel, &user)) \
{ \
ULARGE_INTEGER ullUser; \
{ \
ullUser.LowPart = user.dwLowDateTime; \
ullUser.HighPart = user.dwHighDateTime; \
} \
\
ULARGE_INTEGER ullKernel; \
{ \
ullKernel.LowPart = kernel.dwLowDateTime; \
ullKernel.HighPart = kernel.dwHighDateTime; \
} \
return (expr) * 100ull; \
} \
return HighResClockNS(); \
} \
\
AUKN_SYM AuUInt64 fn ## Clock() \
{ \
FILETIME creation, exit, kernel, user; \
\
if (::Get ## type ## Times(GetCurrent ## type(), &creation, &exit, &kernel, &user)) \
{ \
ULARGE_INTEGER ullUser; \
{ \
ullUser.LowPart = user.dwLowDateTime; \
ullUser.HighPart = user.dwHighDateTime; \
} \
\
ULARGE_INTEGER ullKernel; \
{ \
ullKernel.LowPart = kernel.dwLowDateTime; \
ullKernel.HighPart = kernel.dwHighDateTime; \
} \
return expr; \
} \
\
return fn ##ClockNS() / (1000000000ull / fn ## ClockJiffies()); \
} \
\
AUKN_SYM AuUInt64 fn ## ClockJiffies() \
{ \
return 1000000000ull / 100u; \
}
#elif defined(AURORA_IS_POSIX_DERIVED)
#define ADD_CLOCK_FAMILY(fn, type, expr, posixId, posixCall) \
AUKN_SYM AuUInt64 fn ## ClockJiffies(); \
\
AUKN_SYM AuUInt64 fn ## ClockMS() \
{ \
if (!posixId) \
{ \
return AuNSToMS<AuUInt64>(GetPOSIXTime AU_WHAT(posixCall)); \
} \
return AuNSToMS<AuUInt64>(fn ## ClockNS()); \
} \
\
AUKN_SYM AuUInt64 fn ## ClockNS() \
{ \
if (!posixId) \
{ \
return GetPOSIXTime AU_WHAT(posixCall); \
} \
::timespec spec {}; \
if (::clock_gettime(posixId, &spec) == 0) \
{ \
return AuMSToNS<AuUInt64>(AuSToMS<AuUInt64>(spec.tv_sec)) + (AuUInt64)spec.tv_nsec; \
} \
return HighResClockNS(); \
} \
\
AUKN_SYM AuUInt64 fn ## Clock() \
{ \
if (!posixId) \
{ \
return fn ##ClockNS() / 1000ull; \
} \
return fn ##ClockNS() / (1000000000ull / fn ## ClockJiffies()); \
} \
\
AUKN_SYM AuUInt64 fn ## ClockJiffies() \
{ \
if (!posixId) \
{ \
return 1'000'000ull; \
} \
static AuUInt64 frequency = 0; \
if (frequency != 0) \
{ \
return frequency; \
} \
\
::timespec spec {}; \
if (::clock_getres(posixId, &spec) == 0) \
{ \
if (spec.tv_nsec && !spec.tv_sec) \
{ \
return frequency = 1000000000ull / spec.tv_nsec; \
} \
else \
{ \
SysUnreachable(); \
return 0; \
} \
} \
\
return HighResClockJiffies(); \
}
#else
AUKN_SYM AuUInt64 fn ## ClockMS()
{
return 0;
}
AUKN_SYM AuUInt64 fn ## ClockNS()
{
return 0;
}
AUKN_SYM AuUInt64 fn ## Clock()
{
return 0;
}
AUKN_SYM AuUInt64 fn ## ClockJiffies()
{
return 0;
}
#endif
ADD_CLOCK_FAMILY(Process, Process, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_PROCESS_CPUTIME_ID, (false, EPseudoPosixClock::eAll));
ADD_CLOCK_FAMILY(ProcessKernel, Process, (ullKernel.QuadPart), 0, (false, EPseudoPosixClock::eKernel));
ADD_CLOCK_FAMILY(ProcessUser, Process, (ullUser.QuadPart), CLOCK_PROCESS_CPUTIME_ID, (false, EPseudoPosixClock::eUser));
ADD_CLOCK_FAMILY(Thread, Thread, (ullUser.QuadPart + ullKernel.QuadPart), CLOCK_THREAD_CPUTIME_ID, (true, EPseudoPosixClock::eAll));
ADD_CLOCK_FAMILY(ThreadKernel, Thread, (ullKernel.QuadPart), 0, (true, EPseudoPosixClock::eKernel));
ADD_CLOCK_FAMILY(ThreadUser, Thread, (ullUser.QuadPart), CLOCK_THREAD_CPUTIME_ID, (true, EPseudoPosixClock::eUser));
AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::milliseconds(in) + gUnixDelta).count();
}
AUKN_SYM AuInt64 ConvertAuroraToUnixNS(AuInt64 in)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::nanoseconds(in) + gUnixDelta).count();
}
AUKN_SYM AuInt64 ConvertUnixToAuroraMS(AuInt64 in)
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::milliseconds(in) - gUnixDelta).count();
}
AUKN_SYM AuInt64 ConvertUnixToAuroraNS(AuInt64 in)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::nanoseconds(in) - gUnixDelta).count();
}
AUKN_SYM AuUInt64 SteadyClockJiffies()
{
static AuUInt64 frequency = 0;
if (frequency != 0)
{
return frequency;
}
#if defined(AURORA_COMPILER_MSVC)
return frequency = _Query_perf_frequency();
#endif
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_getres(CLOCK_MONOTONIC, &spec) == 0)
{
if (spec.tv_nsec && !spec.tv_sec)
{
return frequency = 1000000000ull / spec.tv_nsec;
}
}
#endif
return frequency = static_cast<double>(steady_clock::period::den) / static_cast<double>(steady_clock::period::num);
}
AUKN_SYM tm ToCivilTime(AuInt64 time, bool UTC)
{
std::tm ret {};
auto timet = MSToCTime(time);
if (UTC)
{
#if defined(AURORA_COMPILER_MSVC)
auto tm = gmtime_s(&ret, &timet);
#else
auto tm = gmtime_r(&timet, &ret);
#endif
#if defined(AURORA_COMPILER_MSVC)
SysAssert(!tm, "couldn't convert civil time");
#else
SysAssert(tm, "couldn't convert civil time");
#endif
}
else
{
#if defined(AURORA_COMPILER_MSVC)
if (localtime_s(&ret, &timet))
#else
if (!localtime_r(&timet, &ret))
#endif
{
SysPushErrorGeneric("Couldn't convert local civil time");
return ToCivilTime(time, true);
}
}
tm _;
_.CopyFrom(ret);
return _;
}
AUKN_SYM AuInt64 FromCivilTime(const tm &time, bool UTC)
{
::tm tm;
time_t timet;
time.CopyTo(tm);
if (UTC)
{
tm.tm_isdst = 0;
timet = timegm(&tm);
}
else
{
tm.tm_isdst = -1; // out of the 2 crts i've bothered to check, out of 3, this is legal
timet = mktime(&tm);
}
if ((timet == 0) || (timet == -1))
{
return 0;
}
return std::chrono::duration_cast<std::chrono::milliseconds>(NormalizeEpoch(std::chrono::system_clock::from_time_t(timet).time_since_epoch())).count();
}
AUKN_SYM tm NormalizeCivilTimezone(const Time::tm &time, ETimezoneShift shift)
{
if ((time.tm_isdst == 0) && (shift == ETimezoneShift::eUTC))
{
return time;
}
return ToCivilTime(FromCivilTime(time, shift == ETimezoneShift::eUTC));
}
#pragma region TO_DEPRECATE
// [soon to be] @deprecated / was used by benchmarks to measure thread time
AUKN_SYM AuUInt64 HighResClock()
{
return SteadyClock();
}
AUKN_SYM AuUInt64 HighResClockMS()
{
return SteadyClockMS();
}
AUKN_SYM AuUInt64 HighResClockNS()
{
return SteadyClockNS();
}
AUKN_SYM AuUInt64 HighResClockJiffies()
{
return SteadyClockJiffies();
}
#pragma endregion TO_DEPRECATE
}