[*] Begin enforcing steady time

[+] IOProcessor::WakeupThread
[+] NT: Begin hacking in timeBeginPeriod (must spam it in some places)
[+] ConsoleTTY (more specifically the win32 calls) are too slow to run on the mainthread. Delegate to worker.
[*] AuTime.CurrentClockSteady
[*] AuTime.CurrentClockSteadyMS
[*] AuTime.CurrentClockSteadyNS
This commit is contained in:
Reece Wilson 2022-11-28 16:01:08 +00:00
parent bb9c383aee
commit 72dc0d715e
22 changed files with 311 additions and 105 deletions

View File

@ -61,7 +61,8 @@
"ws2_32.lib",
"Ntdll.lib",
"Wer.lib",
"wintrust.lib"
"wintrust.lib",
"Winmm.lib"
]
}
},

View File

@ -41,6 +41,8 @@ namespace Aurora::IO
virtual bool SubmitIOWorkItem (const AuSPtr<IIOProcessorWorkUnit> &work) = 0;
virtual void WakeupThread () = 0;
// Inter-frame callbacks indicating point of execution throughout the [START](optional [yield])[IO][IO TICK][WORK ITEMS][EPILOGUE][END OF FRAME] frame
virtual bool AddEventListener (const AuSPtr<IIOProcessorEventListener> &eventListener) = 0;
virtual void RemoveEventListener (const AuSPtr<IIOProcessorEventListener> &eventListener) = 0;

View File

@ -44,6 +44,12 @@ namespace Aurora::Time
*/
AUKN_SYM AuUInt64 CurrentClockNS();
AUKN_SYM AuUInt64 CurrentClockSteady();
AUKN_SYM AuUInt64 CurrentClockSteadyMS();
AUKN_SYM AuUInt64 CurrentClockSteadyNS();
/**
Translates the Aurora epoch to the standard unix epoch
*/

View File

@ -317,7 +317,7 @@ namespace Aurora::Async
}
else
{
success |= PollInternal(block);
success |= PollInternal(false);
}
}
else
@ -445,7 +445,7 @@ namespace Aurora::Async
return false;
}
} while (state->pendingWorkItems.empty());
} while (state->pendingWorkItems.empty() && block);
if (group->workQueue.empty())
{
@ -537,6 +537,7 @@ namespace Aurora::Async
tlsCallStack++;
//SysBenchmark(fmt::format("RunAsync: {}", block));
// Dispatch
oops->RunAsync();

View File

@ -26,6 +26,7 @@
#include "SWInfo/AuSWInfo.hpp"
#if defined(AURORA_PLATFORM_WIN32)
#include "Extensions/Win32/DarkTheme.hpp"
#include <timeapi.h>
#endif
#include "Process/Process.hpp"
#include "Exit/AuExit.hpp"
@ -49,6 +50,8 @@ static void Init()
gRuntimeRunLevel = 1;
tlsHackIsMainThread = true;
#if defined(AURORA_PLATFORM_WIN32)
::timeBeginPeriod(1);
Aurora::Extensions::Win32::InitDarkMode();
#endif
@ -85,6 +88,10 @@ static void Pump()
#if defined(AURORA_IS_LINUX_DERIVED)
::LinuxSuperSecretIOTick();
#endif
#if defined(AURORA_PLATFORM_WIN32)
::timeBeginPeriod(1);
#endif
}
static void RuntimeLateClean();

View File

@ -97,15 +97,26 @@ namespace Aurora::Console
Logging::InitLoggers();
}
void Pump()
void PumpOnMain()
{
Commands::PumpCommands();
ConsoleStd::Pump();
}
// dumb
void PumpOffMain()
{
ConsoleWxWidgets::Pump();
ConsoleFIO::Pump();
ConsoleTTY::Pump();
}
void Pump()
{
PumpOnMain();
PumpOffMain();
}
void Exit()
{
gDefaultSinks.clear();

View File

@ -17,4 +17,7 @@ namespace Aurora::Console
void Init2();
void Pump();
void Exit();
void PumpOffMain();
void PumpOnMain();
}

View File

@ -21,6 +21,9 @@
namespace Aurora::Console::ConsoleTTY
{
static AuThreads::ThreadUnique_t gConsoleTTYThread;
static bool gMultiThreadTTY { true };
static bool gBuggyFastAppend { false };
#if 1
@ -715,6 +718,7 @@ namespace Aurora::Console::ConsoleTTY
void TTYConsole::Pump()
{
//SysBenchmark("Console TTY pump");
if (!gTTYConsoleEnabled)
{
return;
@ -722,13 +726,20 @@ namespace Aurora::Console::ConsoleTTY
if (NoncanonicalMode())
{
//SysBenchmark("NoncanonicalTick");
NoncanonicalTick();
}
{
//SysBenchmark("flush (redraw)");
Flush();
}
{
//SysBenchmark("history");
PumpHistory();
}
}
void TTYConsole::OnEnter()
{
@ -862,6 +873,10 @@ namespace Aurora::Console::ConsoleTTY
if (iScrollPos == -1)
{
if (this->screenBuffer.size() > maxLines)
{
// TODO (Reece): Removing optimization bc it was broken. This is a nightmare.
if (!gBuggyFastAppend)
{
static bool bSingleShot {};
if (!AuExchange(bSingleShot, true))
@ -869,7 +884,6 @@ namespace Aurora::Console::ConsoleTTY
EnterScrollMode();
}
// TODO (Reece): Removing optimization bc it was broken. This is a nightmare.
auto indexBeforePadding = (this->GetBannerFootBorder() ? 1 + this->topLogPadding : 0) + this->topLogPaddingExtra;
auto startIndex = this->GetTopBorder() + this->GetBannerLines() + indexBeforePadding;
@ -883,6 +897,26 @@ namespace Aurora::Console::ConsoleTTY
return true;
}
else
{
// this is still buggy as shit but we NEEEEED it
// youll notice whitespace appear as soon as we start overflowing but eh
for (int i = startIndex + maxLines; i < this->currentHeight; i++)
{
this->BlankLine(i);
}
TTYScrollBuffer(delta);
for (int i = indexBeforePadding; i < startIndex; i++)
{
this->BlankLine(i);
}
forceRedrawIfFalse = true;
drawPos = startIndex + maxLines - delta;
}
}
else
{
drawPos = startIndex + oldSize;
}
@ -1044,12 +1078,14 @@ namespace Aurora::Console::ConsoleTTY
}
{
// Account for race conditions of size-change during draw
if (PermitDoubleBuffering())
{
bTryAgain = !EndBuffering();
}
}
}
this->bTriggerRedraw = false;
}
@ -1981,9 +2017,37 @@ namespace Aurora::Console::ConsoleTTY
return this->topLogPaddingExtra;
}
static void MainTTY()
{
while (AuIsThreadRunning())
{
AuThreading::Sleep(1000 / 20);
gTTYConsole.Pump();
}
}
void Init()
{
gTTYConsole.Init();
if (gMultiThreadTTY)
{
gConsoleTTYThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(MainTTY)),
AuThreads::IThreadVectorsFunctional::OnExit_t {}),
"ConsoleTTY pumper (win32 is slow as shit ok?)"
));
if (!gConsoleTTYThread)
{
gMultiThreadTTY = false;
return;
}
gConsoleTTYThread->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gConsoleTTYThread->SetPriority(AuThreads::EThreadPriority::ePrioHigh);
gConsoleTTYThread->Run();
}
}
void Exit()
@ -1997,9 +2061,13 @@ namespace Aurora::Console::ConsoleTTY
}
void Pump()
{
// :(
if (!gMultiThreadTTY)
{
gTTYConsole.Pump();
}
}
AUKN_SYM AuSPtr<ITTYConsole> GetTTYConsole()
{

View File

@ -17,6 +17,7 @@
#include <Source/Console/ConsoleFIO/ConsoleFIO.hpp>
#include <Source/RuntimeInternal.hpp>
#include <Source/Console/Flusher.hpp>
#include <Source/Console/Console.hpp>
#if defined(AURORA_IS_LINUX_DERIVED)
void LinuxSuperSecretIOTick();
@ -118,7 +119,7 @@ namespace Aurora::Grug
return;
}
// he's a lazy bastard (ira type insult)
// he's a lazy bastard
gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow);
gGrugsBigWorld->Run();
@ -200,6 +201,7 @@ namespace Aurora::Grug
void GrugFlushFlushs()
{
Logging::ForceFlushFlush();
Console::ForceFlush();
Aurora::Console::PumpOffMain();
//Console::ForceFlush();
}
}

View File

@ -478,9 +478,12 @@ namespace Aurora::IO
{
AU_LOCK_GUARD(this->items.mutex); // < critical section / reentrant | can nest submission
this->items.cvEvent->Set();
auto bRet = AuTryInsert(this->workUnits, work);
//this->pLoopQueue->Commit(); // required to wake up async threads
return bRet;
return AuTryInsert(this->workUnits, work);
}
void IOProcessor::WakeupThread()
{
this->items.cvEvent->Set();
}
bool IOProcessor::AddEventListener(const AuSPtr<IIOProcessorEventListener> &eventListener)

View File

@ -81,6 +81,8 @@ namespace Aurora::IO
bool HasItems() override;
void WakeupThread();
bool CheckThread();
bool RequestRemovalForItemFromAnyThread(const AuSPtr<IIOProcessorItem> &processor);

View File

@ -220,7 +220,7 @@ namespace Aurora::IO::Loop
if (ms)
{
src->timeoutAbs = (AuUInt64)ms + AuTime::CurrentClockMS();
src->timeoutAbs = (AuUInt64)ms + AuTime::CurrentClockSteadyMS();
}
if (!AuTryInsert(this->sources_, src))
@ -426,7 +426,7 @@ namespace Aurora::IO::Loop
AuUInt64 timeout {timeoutIn};
if (timeout)
{
timeout += AuTime::CurrentClockMS();
timeout += AuTime::CurrentClockSteadyMS();
}
{
@ -461,7 +461,7 @@ namespace Aurora::IO::Loop
// but this hack should apply to wait any as well, so i'm moving it to the DoTick function
anythingLeft = epollReference.startingWorkRead.size() || epollReference.startingWorkWrite.size();
bTimeout = timeout ? AuTime::CurrentClockMS() >= timeout : false;
bTimeout = timeout ? AuTime::CurrentClockSteadyMS() >= timeout : false;
} while (anythingLeft && !bTimeout);
{
@ -478,7 +478,7 @@ namespace Aurora::IO::Loop
if (timeout)
{
timeout += AuTime::CurrentClockMS();
timeout += AuTime::CurrentClockSteadyMS();
}
AuUInt32 cTicked {};
@ -532,7 +532,7 @@ namespace Aurora::IO::Loop
if (timeout)
{
timeout += AuTime::CurrentClockMS();
timeout += AuTime::CurrentClockSteadyMS();
}
bool bTryAgain {};
@ -671,28 +671,10 @@ namespace Aurora::IO::Loop
}
}
// syscall epoll_pwait2 is fucking broken and the dipshits who wrote the test used relative values
//
// Nothing I tried worked.
//
// Am I stupid? Probably, but...
// (1) no one as far as i can tell has ever written anything using this syscall, per a github search
// (2) i found one reference that the that are the linux kernel developers used MONO time for this
// one timespec API unlike everything else; using an abs value rel to that clock didn't change
// anything.
// (3) i found a test that would indicate its relative despite the fact UNIX/Linux sync APIs
// tend to use abs time
//
// What does my experience working on xenus tell me?
// Because the gOOOOglers in the form of linux kernel developers were faced with an issue that
// couldn't be solved by involve copy/pasting memory map code, making a mess of public headers,
// or taking credit for third party driver code as their own kernel code; they indeed are to
// blame for making my life miserable once again. Something about this aint adding up.
AuInt64 deltaMS = 0;
if (time)
{
deltaMS = AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockMS());
deltaMS = AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockSteadyMS());
if (deltaMS < 0)
{
deltaMS = 0;
@ -771,7 +753,7 @@ namespace Aurora::IO::Loop
}
}
now = AuTime::CurrentClockMS();
now = AuTime::CurrentClockSteadyMS();
if (!bTicked)
{
@ -786,7 +768,7 @@ namespace Aurora::IO::Loop
if (!now)
{
now = AuTime::CurrentClockMS();
now = AuTime::CurrentClockSteadyMS();
}
for (auto itr = this->sources_.begin(); itr != this->sources_.end(); )

View File

@ -43,7 +43,7 @@ namespace Aurora::IO::Loop
return false;
}
if (time < timeoutAbs)
if (time < this->timeoutAbs)
{
return false;
}
@ -240,7 +240,7 @@ namespace Aurora::IO::Loop
{
AU_LOCK_GUARD(this->sourceMutex_);
if (!AuTryInsert(this->addedSources_, AuMakeTuple(source, AuTime::CurrentInternalClockMS() + maxTimeout, SourceCallbacks {})))
if (!AuTryInsert(this->addedSources_, AuMakeTuple(source, AuTime::CurrentClockSteadyMS() + maxTimeout, SourceCallbacks {})))
{
return false;
}
@ -657,7 +657,7 @@ namespace Aurora::IO::Loop
// Le great iterate
Iterator queueIterator(this);
AuSInt indexOffset {};
auto now = AuTime::CurrentInternalClockMS();
auto now = AuTime::CurrentClockSteadyMS();
for (queueIterator.Start(); queueIterator.End() != queueIterator.itr; )
{
auto &source = *queueIterator.itr;

View File

@ -512,9 +512,12 @@ namespace Aurora::IO::UNIX
return false;
}
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout)
{
AuTime::ms2tsabs(&targetTime, timeout);
iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
}
if (!LinuxOverlappedTrySubmitWork())
@ -526,6 +529,17 @@ namespace Aurora::IO::UNIX
do
{
if (timeout)
{
auto delta = iTargetTime - AuTime::CurrentClockSteadyMS();
if (delta <= 0)
{
return dwApcsSent;
}
AuTime::ns2ts(&targetTime, delta);
}
temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0)
@ -600,9 +614,12 @@ namespace Aurora::IO::UNIX
return epoll_wait(epfd, events, maxevents, timeout);
}
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout)
{
AuTime::ms2tsabs(&targetTime, timeout);
iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
}
if (!LinuxOverlappedTrySubmitWork())
@ -640,6 +657,17 @@ namespace Aurora::IO::UNIX
do
{
if (timeout)
{
auto delta = iTargetTime - AuTime::CurrentClockSteadyMS();
if (delta <= 0)
{
return dwApcsSent;
}
AuTime::ns2ts(&targetTime, delta);
}
temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0)
@ -739,9 +767,12 @@ namespace Aurora::IO::UNIX
return false;
}
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout)
{
AuTime::ms2tsabs(&targetTime, timeout);
iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
}
if (!LinuxOverlappedTrySubmitWork())
@ -794,6 +825,17 @@ namespace Aurora::IO::UNIX
do
{
if (timeout)
{
auto delta = iTargetTime - AuTime::CurrentClockSteadyMS();
if (delta <= 0)
{
return dwApcsSent;
}
AuTime::ns2ts(&targetTime, delta);
}
temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0)

View File

@ -15,23 +15,28 @@ namespace Aurora::Threading::Primitives
{
ConditionVariableImpl::ConditionVariableImpl(const AuSPtr<IConditionMutex> &mutex) : mutex_(std::dynamic_pointer_cast<IConditionMutexEx>(mutex))
{
auto ret = pthread_cond_init(&pthreadCv_, NULL);
pthread_condattr_t attr;
::pthread_condattr_init(&attr);
::pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
auto ret = ::pthread_cond_init(&this->pthreadCv_, &attr);
SysAssert(ret == 0, "Couldn't initialize CV");
}
ConditionVariableImpl::~ConditionVariableImpl()
{
pthread_cond_destroy(&pthreadCv_);
::pthread_cond_destroy(&this->pthreadCv_);
}
AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex()
{
return mutex_;
return this->mutex_;
}
bool ConditionVariableImpl::WaitForSignal(AuUInt32 timeout)
{
auto mutex = reinterpret_cast<pthread_mutex_t*>(mutex_->GetOSHandle());
auto mutex = reinterpret_cast<pthread_mutex_t*>(this->mutex_->GetOSHandle());
if (gRuntimeRunLevel >= 5)
{
@ -43,7 +48,7 @@ namespace Aurora::Threading::Primitives
int ret {};
do
{
if ((ret = pthread_cond_wait(&pthreadCv_, mutex)) == 0)
if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
{
return true;
}
@ -61,7 +66,7 @@ namespace Aurora::Threading::Primitives
do
{
ret = pthread_cond_timedwait(&pthreadCv_, mutex, &tspec);
ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
if (ret == 0)
{
@ -82,13 +87,13 @@ namespace Aurora::Threading::Primitives
void ConditionVariableImpl::Signal()
{
auto ret = pthread_cond_signal(&pthreadCv_);
auto ret = ::pthread_cond_signal(&this->pthreadCv_);
SysAssert(ret == 0, "Couldn't wake any CV waiters");
}
void ConditionVariableImpl::Broadcast()
{
auto ret = pthread_cond_broadcast(&pthreadCv_);
auto ret = ::pthread_cond_broadcast(&this->pthreadCv_);
SysAssert(ret == 0, "Couldn't wake any CV waiters");
}

View File

@ -33,7 +33,7 @@ namespace Aurora::Threading::Primitives
{
AU_LOCK_GUARD(this->mutex_);
AuInt64 startTime = Time::CurrentClockMS();
AuInt64 startTime = Time::CurrentClockSteadyMS();
AuInt64 endTime = startTime + timeout;
while (!AtomicIsEventSet())
@ -42,7 +42,7 @@ namespace Aurora::Threading::Primitives
if (timeout)
{
timeoutMs = endTime - static_cast<AuInt64>(Time::CurrentClockMS());
timeoutMs = endTime - static_cast<AuInt64>(Time::CurrentClockSteadyMS());
if (timeoutMs < 0)
{
return false;

View File

@ -15,9 +15,9 @@ namespace Aurora::Threading::Primitives
{
Mutex::Mutex()
{
InitializeSRWLock(&atomicHolder_);
InitializeConditionVariable(&wakeup_);
state_ = 0;
InitializeSRWLock(&this->atomicHolder_);
InitializeConditionVariable(&this->wakeup_);
this->state_ = 0;
}
Mutex::~Mutex()
@ -32,7 +32,7 @@ namespace Aurora::Threading::Primitives
bool Mutex::TryLock()
{
return _interlockedbittestandset(&state_, 0) == 0;
return _interlockedbittestandset(&this->state_, 0) == 0;
}
bool Mutex::HasLockImplementation()
@ -50,9 +50,9 @@ namespace Aurora::Threading::Primitives
{
bool returnValue = false;
AcquireSRWLockShared(&atomicHolder_);
AcquireSRWLockShared(&this->atomicHolder_);
AuInt64 startTime = Time::CurrentClockMS();
AuInt64 startTime = Time::CurrentClockSteadyMS();
AuInt64 endTime = startTime + timeout;
BOOL status = false;
@ -62,7 +62,7 @@ namespace Aurora::Threading::Primitives
if (timeout != 0)
{
startTime = Time::CurrentClockMS();
startTime = Time::CurrentClockSteadyMS();
if (startTime >= endTime)
{
goto exitWin32;
@ -71,7 +71,7 @@ namespace Aurora::Threading::Primitives
timeoutMs = endTime - startTime;
}
status = SleepConditionVariableSRW(&wakeup_, &atomicHolder_, timeoutMs, CONDITION_VARIABLE_LOCKMODE_SHARED);
status = SleepConditionVariableSRW(&this->wakeup_, &this->atomicHolder_, timeoutMs, CONDITION_VARIABLE_LOCKMODE_SHARED);
if (!status)
{
SysAssertExp(GetLastError() == ERROR_TIMEOUT);
@ -82,16 +82,16 @@ namespace Aurora::Threading::Primitives
returnValue = true;
exitWin32:
ReleaseSRWLockShared(&atomicHolder_);
ReleaseSRWLockShared(&this->atomicHolder_);
return returnValue;
}
void Mutex::Unlock()
{
AcquireSRWLockExclusive(&atomicHolder_);
state_ = 0;
ReleaseSRWLockExclusive(&atomicHolder_);
WakeAllConditionVariable(&wakeup_);
AcquireSRWLockExclusive(&this->atomicHolder_);
this->state_ = 0;
ReleaseSRWLockExclusive(&this->atomicHolder_);
WakeAllConditionVariable(&this->wakeup_);
}
AUKN_SYM IWaitable *MutexNew()

View File

@ -68,7 +68,7 @@ namespace Aurora::Threading::Primitives
else
{
struct timespec tspec;
Time::ms2tsabs(&tspec, timeout);
Time::ms2tsabsRealtime(&tspec, timeout);
int ret {};

View File

@ -42,7 +42,7 @@ namespace Aurora::Threading::Primitives
bool Semaphore::Lock(AuUInt64 timeout)
{
AuUInt64 start = AuTime::CurrentInternalClockMS();
AuUInt64 start = AuTime::CurrentClockSteadyMS();
AuUInt64 end = start + timeout;
AcquireSRWLockShared(&lock_); // we use atomics. using shared is fine, let's not get congested early
@ -52,7 +52,7 @@ namespace Aurora::Threading::Primitives
if (timeout != 0)
{
start = Time::CurrentClockMS();
start = Time::CurrentClockSteadyMS();
if (start >= end)
{
ReleaseSRWLockShared(&this->lock_);

View File

@ -63,8 +63,9 @@ namespace Aurora::Threading::Primitives
else
{
struct timespec tspec;
Time::ms2tsabs(&tspec, timeout);
Time::ms2tsabsRealtime(&tspec, timeout);
//
int ret {};
do

View File

@ -4,12 +4,15 @@
File: Clock.cpp
Date: 2021-6-13
Author: Reece
Note: Screw it, std::chrono has been widly shilled at C++11s answer to all these painful macros, asm linkage, and other shit youd have to do to pull clock
Semantics have changed over time. I remember when, in 2016 or something like this, msvcs implementation of chrono kept breaking things.
Every platform that remotely pretends to support a C++ toolchain has a chrono high performance clock. Any PC-like platform that uses clang and C++ more than likely uses a vendor hacked libc++ stl.
Seems portable enough. Worst case scenario, you're on a platform with a platform-specific high res clock function you could hack into here alongside portable timezone-unaware timegm/mktime functions somewhere else.
There is so much quirky shit one has to deal with when relying on instruction cycle counters and other such instructions, plus cycle to ~time pred, plus forced + direction only, atop non-standard [sometimes inline]
assembly, it's just not worth it to implement a CNTVCT_EL0 / RDSC / related userland client-app attempt of a high res clock.
Note: Screw it, std::chrono has been widly shilled at C++11s answer to all these painful macros, asm linkage, and all the other bullshit that pulling clock counters entails.
Semantics can and will continue to change over time. I remember when, in 2016 or something like that, msvcs implementation of chrono kept changing in minor ways.
However, every platform that remotely pretends to support a C++ toolchain has a chrono high performance clock, and any PC-like platform that uses clang and C++ more than likely uses a vendor hacked liblibc++ stl.
The following should be portable enough. Worst case scenario, you're on a platform with a platform-specific high res clock function you could hack into here alongside portable timezone-unaware timegm/mktime functions linked somewhere else.
There is so much quirky shit one has to deal with when relying on timestamp/cycle counters (cycle to ~time pred, plus positive delta, sometimes inlined assembly), it's just not worth it to
implement a CNTVCT_EL0 / RDSC / related interface ourselves.
I'll wave the white flag and use the STL in here for.now
***/
#include <Source/RuntimeInternal.hpp>
@ -70,6 +73,7 @@ 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
@ -155,6 +159,45 @@ namespace Aurora::Time
return std::chrono::duration_cast<std::chrono::nanoseconds>(NormalizeEpoch(sys_clock::now().time_since_epoch())).count();
}
AUKN_SYM AuUInt64 CurrentClockSteady()
{
return CurrentClockSteadyNS();
}
AUKN_SYM AuUInt64 CurrentClockSteadyMS()
{
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_getclock(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 CurrentClockSteadyNS()
{
#if defined(AURORA_IS_POSIX_DERIVED)
::timespec spec {};
if (::clock_getclock(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();
@ -246,6 +289,22 @@ namespace Aurora::Time
{
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>(high_res_clock::period::den) / static_cast<double>(high_res_clock::period::num);
}

View File

@ -54,7 +54,18 @@ namespace Aurora::Time
static void ms2tsabs(struct timespec *ts, unsigned long ms)
{
clock_gettime(CLOCK_REALTIME, ts);
::clock_gettime(CLOCK_MONOTONIC, ts);
auto baseNS = ((AuUInt64)ms * (AuUInt64)1'000'000) + (AuUInt64)ts->tv_nsec;
auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000;
ts->tv_sec += baseNS / 1'000'000'000ull;
ts->tv_nsec = remainderNS;
}
static void ms2tsabsRealtime(struct timespec *ts, unsigned long ms)
{
::clock_gettime(CLOCK_REALTIME, ts);
auto baseNS = ((AuUInt64)ms * (AuUInt64)1'000'000) + (AuUInt64)ts->tv_nsec;
auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000;
@ -80,7 +91,7 @@ namespace Aurora::Time
static void ms2tvabs(struct timeval *tv, unsigned long ms)
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
::clock_gettime(CLOCK_REALTIME, &ts);
auto baseNS = ((AuUInt64)ms * (AuUInt64)1'000'000) + (AuUInt64)ts.tv_nsec;
auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000;