[*] 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", "ws2_32.lib",
"Ntdll.lib", "Ntdll.lib",
"Wer.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 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 // 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 bool AddEventListener (const AuSPtr<IIOProcessorEventListener> &eventListener) = 0;
virtual void RemoveEventListener (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 CurrentClockNS();
AUKN_SYM AuUInt64 CurrentClockSteady();
AUKN_SYM AuUInt64 CurrentClockSteadyMS();
AUKN_SYM AuUInt64 CurrentClockSteadyNS();
/** /**
Translates the Aurora epoch to the standard unix epoch Translates the Aurora epoch to the standard unix epoch
*/ */

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,9 @@
namespace Aurora::Console::ConsoleTTY namespace Aurora::Console::ConsoleTTY
{ {
static AuThreads::ThreadUnique_t gConsoleTTYThread;
static bool gMultiThreadTTY { true };
static bool gBuggyFastAppend { false };
#if 1 #if 1
@ -715,6 +718,7 @@ namespace Aurora::Console::ConsoleTTY
void TTYConsole::Pump() void TTYConsole::Pump()
{ {
//SysBenchmark("Console TTY pump");
if (!gTTYConsoleEnabled) if (!gTTYConsoleEnabled)
{ {
return; return;
@ -722,12 +726,19 @@ namespace Aurora::Console::ConsoleTTY
if (NoncanonicalMode()) if (NoncanonicalMode())
{ {
//SysBenchmark("NoncanonicalTick");
NoncanonicalTick(); NoncanonicalTick();
} }
Flush(); {
//SysBenchmark("flush (redraw)");
PumpHistory(); Flush();
}
{
//SysBenchmark("history");
PumpHistory();
}
} }
void TTYConsole::OnEnter() void TTYConsole::OnEnter()
@ -799,30 +810,30 @@ namespace Aurora::Console::ConsoleTTY
while (str.size()) while (str.size())
{ {
int XOffset = GetLeftBorder() + this->leftLogPadding; int XOffset = GetLeftBorder() + this->leftLogPadding;
auto itr = str.npos; auto itr = str.npos;
itr = str.find('\t'); itr = str.find('\t');
while (itr != str.npos) while (itr != str.npos)
{ {
auto suffix = str.substr(itr + 1); auto suffix = str.substr(itr + 1);
str = str.substr(0, itr); str = str.substr(0, itr);
AuString padding(4 - (itr % 4), ' '); AuString padding(4 - (itr % 4), ' ');
str += padding; str += padding;
str += suffix; str += suffix;
itr = str.find('\t', itr); itr = str.find('\t', itr);
} }
int anyLineRemoveEsc = str.rfind('\x1b', 0) == 0; int anyLineRemoveEsc = str.rfind('\x1b', 0) == 0;
int idx = AuLocale::Encoding::CountUTF8Length({str.data(), AuMin<AuUInt>(str.size(), maxWidth + (anyLineRemoveEsc * 7))}, true); int idx = AuLocale::Encoding::CountUTF8Length({str.data(), AuMin<AuUInt>(str.size(), maxWidth + (anyLineRemoveEsc * 7))}, true);
auto append = str.substr(0, idx); auto append = str.substr(0, idx);
AuTryInsert(this->screenBuffer, append); AuTryInsert(this->screenBuffer, append);
str = str.substr(idx); str = str.substr(idx);
this->bScreenBufferDoesntMap |= bool(str.size()); this->bScreenBufferDoesntMap |= bool(str.size());
} }
@ -863,24 +874,47 @@ namespace Aurora::Console::ConsoleTTY
{ {
if (this->screenBuffer.size() > maxLines) if (this->screenBuffer.size() > maxLines)
{ {
static bool bSingleShot {};
if (!AuExchange(bSingleShot, true))
{
EnterScrollMode();
}
// TODO (Reece): Removing optimization bc it was broken. This is a nightmare. // 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;
for (int i = startIndex; i < this->currentHeight; i++) if (!gBuggyFastAppend)
{ {
this->BlankLine(i); static bool bSingleShot {};
} if (!AuExchange(bSingleShot, true))
{
EnterScrollMode();
}
this->screenBuffer.clear(); auto indexBeforePadding = (this->GetBannerFootBorder() ? 1 + this->topLogPadding : 0) + this->topLogPaddingExtra;
addMessages(this->messages); auto startIndex = this->GetTopBorder() + this->GetBannerLines() + indexBeforePadding;
return true;
for (int i = startIndex; i < this->currentHeight; i++)
{
this->BlankLine(i);
}
this->screenBuffer.clear();
addMessages(this->messages);
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 else
{ {
@ -1044,10 +1078,12 @@ namespace Aurora::Console::ConsoleTTY
} }
// Account for race conditions of size-change during draw
if (PermitDoubleBuffering())
{ {
bTryAgain = !EndBuffering(); // Account for race conditions of size-change during draw
if (PermitDoubleBuffering())
{
bTryAgain = !EndBuffering();
}
} }
} }
@ -1981,9 +2017,37 @@ namespace Aurora::Console::ConsoleTTY
return this->topLogPaddingExtra; return this->topLogPaddingExtra;
} }
static void MainTTY()
{
while (AuIsThreadRunning())
{
AuThreading::Sleep(1000 / 20);
gTTYConsole.Pump();
}
}
void Init() void Init()
{ {
gTTYConsole.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() void Exit()
@ -1998,7 +2062,11 @@ namespace Aurora::Console::ConsoleTTY
void Pump() void Pump()
{ {
gTTYConsole.Pump(); // :(
if (!gMultiThreadTTY)
{
gTTYConsole.Pump();
}
} }
AUKN_SYM AuSPtr<ITTYConsole> GetTTYConsole() AUKN_SYM AuSPtr<ITTYConsole> GetTTYConsole()

View File

@ -17,6 +17,7 @@
#include <Source/Console/ConsoleFIO/ConsoleFIO.hpp> #include <Source/Console/ConsoleFIO/ConsoleFIO.hpp>
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include <Source/Console/Flusher.hpp> #include <Source/Console/Flusher.hpp>
#include <Source/Console/Console.hpp>
#if defined(AURORA_IS_LINUX_DERIVED) #if defined(AURORA_IS_LINUX_DERIVED)
void LinuxSuperSecretIOTick(); void LinuxSuperSecretIOTick();
@ -118,7 +119,7 @@ namespace Aurora::Grug
return; return;
} }
// he's a lazy bastard (ira type insult) // he's a lazy bastard
gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient); gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow); gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow);
gGrugsBigWorld->Run(); gGrugsBigWorld->Run();
@ -200,6 +201,7 @@ namespace Aurora::Grug
void GrugFlushFlushs() void GrugFlushFlushs()
{ {
Logging::ForceFlushFlush(); 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 AU_LOCK_GUARD(this->items.mutex); // < critical section / reentrant | can nest submission
this->items.cvEvent->Set(); this->items.cvEvent->Set();
auto bRet = AuTryInsert(this->workUnits, work); return AuTryInsert(this->workUnits, work);
//this->pLoopQueue->Commit(); // required to wake up async threads }
return bRet;
void IOProcessor::WakeupThread()
{
this->items.cvEvent->Set();
} }
bool IOProcessor::AddEventListener(const AuSPtr<IIOProcessorEventListener> &eventListener) bool IOProcessor::AddEventListener(const AuSPtr<IIOProcessorEventListener> &eventListener)

View File

@ -80,6 +80,8 @@ namespace Aurora::IO
void ReleaseAllWatches() override; void ReleaseAllWatches() override;
bool HasItems() override; bool HasItems() override;
void WakeupThread();
bool CheckThread(); bool CheckThread();

View File

@ -220,7 +220,7 @@ namespace Aurora::IO::Loop
if (ms) if (ms)
{ {
src->timeoutAbs = (AuUInt64)ms + AuTime::CurrentClockMS(); src->timeoutAbs = (AuUInt64)ms + AuTime::CurrentClockSteadyMS();
} }
if (!AuTryInsert(this->sources_, src)) if (!AuTryInsert(this->sources_, src))
@ -426,7 +426,7 @@ namespace Aurora::IO::Loop
AuUInt64 timeout {timeoutIn}; AuUInt64 timeout {timeoutIn};
if (timeout) 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 // 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(); anythingLeft = epollReference.startingWorkRead.size() || epollReference.startingWorkWrite.size();
bTimeout = timeout ? AuTime::CurrentClockMS() >= timeout : false; bTimeout = timeout ? AuTime::CurrentClockSteadyMS() >= timeout : false;
} while (anythingLeft && !bTimeout); } while (anythingLeft && !bTimeout);
{ {
@ -478,7 +478,7 @@ namespace Aurora::IO::Loop
if (timeout) if (timeout)
{ {
timeout += AuTime::CurrentClockMS(); timeout += AuTime::CurrentClockSteadyMS();
} }
AuUInt32 cTicked {}; AuUInt32 cTicked {};
@ -532,7 +532,7 @@ namespace Aurora::IO::Loop
if (timeout) if (timeout)
{ {
timeout += AuTime::CurrentClockMS(); timeout += AuTime::CurrentClockSteadyMS();
} }
bool bTryAgain {}; 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; AuInt64 deltaMS = 0;
if (time) if (time)
{ {
deltaMS = AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockMS()); deltaMS = AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockSteadyMS());
if (deltaMS < 0) if (deltaMS < 0)
{ {
deltaMS = 0; deltaMS = 0;
@ -771,7 +753,7 @@ namespace Aurora::IO::Loop
} }
} }
now = AuTime::CurrentClockMS(); now = AuTime::CurrentClockSteadyMS();
if (!bTicked) if (!bTicked)
{ {
@ -786,7 +768,7 @@ namespace Aurora::IO::Loop
if (!now) if (!now)
{ {
now = AuTime::CurrentClockMS(); now = AuTime::CurrentClockSteadyMS();
} }
for (auto itr = this->sources_.begin(); itr != this->sources_.end(); ) for (auto itr = this->sources_.begin(); itr != this->sources_.end(); )

View File

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

View File

@ -512,9 +512,12 @@ namespace Aurora::IO::UNIX
return false; return false;
} }
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout) if (timeout)
{ {
AuTime::ms2tsabs(&targetTime, timeout); iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
} }
if (!LinuxOverlappedTrySubmitWork()) if (!LinuxOverlappedTrySubmitWork())
@ -526,6 +529,17 @@ namespace Aurora::IO::UNIX
do 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); temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0) if (temp >= 0)
@ -600,9 +614,12 @@ namespace Aurora::IO::UNIX
return epoll_wait(epfd, events, maxevents, timeout); return epoll_wait(epfd, events, maxevents, timeout);
} }
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout) if (timeout)
{ {
AuTime::ms2tsabs(&targetTime, timeout); iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
} }
if (!LinuxOverlappedTrySubmitWork()) if (!LinuxOverlappedTrySubmitWork())
@ -640,6 +657,17 @@ namespace Aurora::IO::UNIX
do 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); temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0) if (temp >= 0)
@ -739,9 +767,12 @@ namespace Aurora::IO::UNIX
return false; return false;
} }
AuInt64 iStartTime {};
AuInt64 iTargetTime {};
if (timeout) if (timeout)
{ {
AuTime::ms2tsabs(&targetTime, timeout); iStartTime = AuTime::CurrentClockSteadyMS();
iTargetTime = iStartTime + timeout;
} }
if (!LinuxOverlappedTrySubmitWork()) if (!LinuxOverlappedTrySubmitWork())
@ -794,6 +825,17 @@ namespace Aurora::IO::UNIX
do 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); temp = io_getevents(io->context, 1, 512, ioEvents, timeout ? &targetTime : nullptr);
if (temp >= 0) 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)) 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"); SysAssert(ret == 0, "Couldn't initialize CV");
} }
ConditionVariableImpl::~ConditionVariableImpl() ConditionVariableImpl::~ConditionVariableImpl()
{ {
pthread_cond_destroy(&pthreadCv_); ::pthread_cond_destroy(&this->pthreadCv_);
} }
AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex() AuSPtr<IConditionMutex> ConditionVariableImpl::GetMutex()
{ {
return mutex_; return this->mutex_;
} }
bool ConditionVariableImpl::WaitForSignal(AuUInt32 timeout) 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) if (gRuntimeRunLevel >= 5)
{ {
@ -43,7 +48,7 @@ namespace Aurora::Threading::Primitives
int ret {}; int ret {};
do do
{ {
if ((ret = pthread_cond_wait(&pthreadCv_, mutex)) == 0) if ((ret = ::pthread_cond_wait(&this->pthreadCv_, mutex)) == 0)
{ {
return true; return true;
} }
@ -61,7 +66,7 @@ namespace Aurora::Threading::Primitives
do do
{ {
ret = pthread_cond_timedwait(&pthreadCv_, mutex, &tspec); ret = ::pthread_cond_timedwait(&this->pthreadCv_, mutex, &tspec);
if (ret == 0) if (ret == 0)
{ {
@ -82,13 +87,13 @@ namespace Aurora::Threading::Primitives
void ConditionVariableImpl::Signal() 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"); SysAssert(ret == 0, "Couldn't wake any CV waiters");
} }
void ConditionVariableImpl::Broadcast() 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"); SysAssert(ret == 0, "Couldn't wake any CV waiters");
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,12 +4,15 @@
File: Clock.cpp File: Clock.cpp
Date: 2021-6-13 Date: 2021-6-13
Author: Reece 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 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 have changed over time. I remember when, in 2016 or something like this, msvcs implementation of chrono kept breaking things. 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.
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. 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.
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] 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.
assembly, it's just not worth it to implement a CNTVCT_EL0 / RDSC / related userland client-app attempt of a high res clock.
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 I'll wave the white flag and use the STL in here for.now
***/ ***/
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
@ -70,6 +73,7 @@ using high_res_clock = std::chrono::high_resolution_clock;
#endif #endif
using sys_clock = std::chrono::system_clock; using sys_clock = std::chrono::system_clock;
using steady_clock = std::chrono::steady_clock;
#if defined(AURORA_PLATFORM_WIN32) #if defined(AURORA_PLATFORM_WIN32)
#define timegm _mkgmtime #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(); 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) 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(); 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; 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); return frequency = static_cast<double>(high_res_clock::period::den) / static_cast<double>(high_res_clock::period::num);
} }

View File

@ -54,7 +54,7 @@ namespace Aurora::Time
static void ms2tsabs(struct timespec *ts, unsigned long ms) 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 baseNS = ((AuUInt64)ms * (AuUInt64)1'000'000) + (AuUInt64)ts->tv_nsec;
auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000; auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000;
@ -63,6 +63,17 @@ namespace Aurora::Time
ts->tv_nsec = remainderNS; 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;
ts->tv_sec += baseNS / 1'000'000'000ull;
ts->tv_nsec = remainderNS;
}
static void auabsns2ts(struct timespec *ts, unsigned long long ns) static void auabsns2ts(struct timespec *ts, unsigned long long ns)
{ {
auto baseNS = ns + AuMSToNS<AuUInt64>(999'080'100'000); auto baseNS = ns + AuMSToNS<AuUInt64>(999'080'100'000);
@ -80,7 +91,7 @@ namespace Aurora::Time
static void ms2tvabs(struct timeval *tv, unsigned long ms) static void ms2tvabs(struct timeval *tv, unsigned long ms)
{ {
timespec ts; 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 baseNS = ((AuUInt64)ms * (AuUInt64)1'000'000) + (AuUInt64)ts.tv_nsec;
auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000; auto remainderNS = (AuUInt64)baseNS % (AuUInt64)1'000'000'000;