diff --git a/Aurora.json b/Aurora.json index 14b5a755..ec4b16b4 100644 --- a/Aurora.json +++ b/Aurora.json @@ -61,7 +61,8 @@ "ws2_32.lib", "Ntdll.lib", "Wer.lib", - "wintrust.lib" + "wintrust.lib", + "Winmm.lib" ] } }, diff --git a/Include/Aurora/IO/IIOProcessor.hpp b/Include/Aurora/IO/IIOProcessor.hpp index 3531359b..23e0b25c 100644 --- a/Include/Aurora/IO/IIOProcessor.hpp +++ b/Include/Aurora/IO/IIOProcessor.hpp @@ -41,6 +41,8 @@ namespace Aurora::IO virtual bool SubmitIOWorkItem (const AuSPtr &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 &eventListener) = 0; virtual void RemoveEventListener (const AuSPtr &eventListener) = 0; diff --git a/Include/Aurora/Time/Clock.hpp b/Include/Aurora/Time/Clock.hpp index 61b79392..9548aa1e 100644 --- a/Include/Aurora/Time/Clock.hpp +++ b/Include/Aurora/Time/Clock.hpp @@ -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 */ diff --git a/Source/Async/ThreadPool.cpp b/Source/Async/ThreadPool.cpp index 600efabf..dbeac8cd 100644 --- a/Source/Async/ThreadPool.cpp +++ b/Source/Async/ThreadPool.cpp @@ -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(); diff --git a/Source/AuRTEntrypoint.cpp b/Source/AuRTEntrypoint.cpp index 155d5c53..2a09d33e 100644 --- a/Source/AuRTEntrypoint.cpp +++ b/Source/AuRTEntrypoint.cpp @@ -26,6 +26,7 @@ #include "SWInfo/AuSWInfo.hpp" #if defined(AURORA_PLATFORM_WIN32) #include "Extensions/Win32/DarkTheme.hpp" + #include #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(); diff --git a/Source/Console/Console.cpp b/Source/Console/Console.cpp index 4e568456..97b40f67 100644 --- a/Source/Console/Console.cpp +++ b/Source/Console/Console.cpp @@ -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(); diff --git a/Source/Console/Console.hpp b/Source/Console/Console.hpp index 6ab48f90..19cd4eef 100644 --- a/Source/Console/Console.hpp +++ b/Source/Console/Console.hpp @@ -17,4 +17,7 @@ namespace Aurora::Console void Init2(); void Pump(); void Exit(); + + void PumpOffMain(); + void PumpOnMain(); } \ No newline at end of file diff --git a/Source/Console/ConsoleTTY/ConsoleTTY.cpp b/Source/Console/ConsoleTTY/ConsoleTTY.cpp index 3218ea2e..efe221ed 100644 --- a/Source/Console/ConsoleTTY/ConsoleTTY.cpp +++ b/Source/Console/ConsoleTTY/ConsoleTTY.cpp @@ -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,12 +726,19 @@ namespace Aurora::Console::ConsoleTTY if (NoncanonicalMode()) { + //SysBenchmark("NoncanonicalTick"); NoncanonicalTick(); } - Flush(); - - PumpHistory(); + { + //SysBenchmark("flush (redraw)"); + Flush(); + } + + { + //SysBenchmark("history"); + PumpHistory(); + } } void TTYConsole::OnEnter() @@ -799,30 +810,30 @@ namespace Aurora::Console::ConsoleTTY while (str.size()) { int XOffset = GetLeftBorder() + this->leftLogPadding; - + auto itr = str.npos; itr = str.find('\t'); - + while (itr != str.npos) { auto suffix = str.substr(itr + 1); str = str.substr(0, itr); - + AuString padding(4 - (itr % 4), ' '); - + str += padding; str += suffix; - + itr = str.find('\t', itr); } - + int anyLineRemoveEsc = str.rfind('\x1b', 0) == 0; - + int idx = AuLocale::Encoding::CountUTF8Length({str.data(), AuMin(str.size(), maxWidth + (anyLineRemoveEsc * 7))}, true); auto append = str.substr(0, idx); AuTryInsert(this->screenBuffer, append); - + str = str.substr(idx); this->bScreenBufferDoesntMap |= bool(str.size()); } @@ -863,24 +874,47 @@ namespace Aurora::Console::ConsoleTTY { 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. - 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(); - addMessages(this->messages); - return true; + 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++) + { + 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 { @@ -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; } + 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::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() @@ -1998,7 +2062,11 @@ namespace Aurora::Console::ConsoleTTY void Pump() { - gTTYConsole.Pump(); + // :( + if (!gMultiThreadTTY) + { + gTTYConsole.Pump(); + } } AUKN_SYM AuSPtr GetTTYConsole() diff --git a/Source/Grug/AuGrug.cpp b/Source/Grug/AuGrug.cpp index b0363b6f..422cc30c 100644 --- a/Source/Grug/AuGrug.cpp +++ b/Source/Grug/AuGrug.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #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(); } } \ No newline at end of file diff --git a/Source/IO/IOProcessor.cpp b/Source/IO/IOProcessor.cpp index 2439fbcb..a4518227 100644 --- a/Source/IO/IOProcessor.cpp +++ b/Source/IO/IOProcessor.cpp @@ -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 &eventListener) diff --git a/Source/IO/IOProcessor.hpp b/Source/IO/IOProcessor.hpp index 72d14512..17d57806 100644 --- a/Source/IO/IOProcessor.hpp +++ b/Source/IO/IOProcessor.hpp @@ -80,6 +80,8 @@ namespace Aurora::IO void ReleaseAllWatches() override; bool HasItems() override; + + void WakeupThread(); bool CheckThread(); diff --git a/Source/IO/Loop/LoopQueue.Linux.cpp b/Source/IO/Loop/LoopQueue.Linux.cpp index d9f70472..ac45cb2d 100644 --- a/Source/IO/Loop/LoopQueue.Linux.cpp +++ b/Source/IO/Loop/LoopQueue.Linux.cpp @@ -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(); ) diff --git a/Source/IO/Loop/LoopQueue.NT.cpp b/Source/IO/Loop/LoopQueue.NT.cpp index afb20c26..3bf14752 100644 --- a/Source/IO/Loop/LoopQueue.NT.cpp +++ b/Source/IO/Loop/LoopQueue.NT.cpp @@ -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; diff --git a/Source/IO/UNIX/IOSubmit.Linux.cpp b/Source/IO/UNIX/IOSubmit.Linux.cpp index 3b92b472..80ac5e81 100644 --- a/Source/IO/UNIX/IOSubmit.Linux.cpp +++ b/Source/IO/UNIX/IOSubmit.Linux.cpp @@ -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) diff --git a/Source/Threading/Primitives/AuConditionVariable.Unix.cpp b/Source/Threading/Primitives/AuConditionVariable.Unix.cpp index 77d0488b..5d787648 100644 --- a/Source/Threading/Primitives/AuConditionVariable.Unix.cpp +++ b/Source/Threading/Primitives/AuConditionVariable.Unix.cpp @@ -15,23 +15,28 @@ namespace Aurora::Threading::Primitives { ConditionVariableImpl::ConditionVariableImpl(const AuSPtr &mutex) : mutex_(std::dynamic_pointer_cast(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 ConditionVariableImpl::GetMutex() { - return mutex_; + return this->mutex_; } bool ConditionVariableImpl::WaitForSignal(AuUInt32 timeout) { - auto mutex = reinterpret_cast(mutex_->GetOSHandle()); + auto mutex = reinterpret_cast(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"); } diff --git a/Source/Threading/Primitives/AuEvent.cpp b/Source/Threading/Primitives/AuEvent.cpp index 0dc4f06b..6082cb29 100644 --- a/Source/Threading/Primitives/AuEvent.cpp +++ b/Source/Threading/Primitives/AuEvent.cpp @@ -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(Time::CurrentClockMS()); + timeoutMs = endTime - static_cast(Time::CurrentClockSteadyMS()); if (timeoutMs < 0) { return false; diff --git a/Source/Threading/Primitives/AuMutex.NT.cpp b/Source/Threading/Primitives/AuMutex.NT.cpp index c3883d20..fa985bc2 100644 --- a/Source/Threading/Primitives/AuMutex.NT.cpp +++ b/Source/Threading/Primitives/AuMutex.NT.cpp @@ -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() diff --git a/Source/Threading/Primitives/AuMutex.Unix.cpp b/Source/Threading/Primitives/AuMutex.Unix.cpp index 18d5cf5a..4a62656c 100644 --- a/Source/Threading/Primitives/AuMutex.Unix.cpp +++ b/Source/Threading/Primitives/AuMutex.Unix.cpp @@ -68,7 +68,7 @@ namespace Aurora::Threading::Primitives else { struct timespec tspec; - Time::ms2tsabs(&tspec, timeout); + Time::ms2tsabsRealtime(&tspec, timeout); int ret {}; diff --git a/Source/Threading/Primitives/AuSemaphore.NT.cpp b/Source/Threading/Primitives/AuSemaphore.NT.cpp index f7549cc3..9721e9eb 100644 --- a/Source/Threading/Primitives/AuSemaphore.NT.cpp +++ b/Source/Threading/Primitives/AuSemaphore.NT.cpp @@ -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_); diff --git a/Source/Threading/Primitives/AuSemaphore.Unix.cpp b/Source/Threading/Primitives/AuSemaphore.Unix.cpp index f8033402..8c2f3092 100644 --- a/Source/Threading/Primitives/AuSemaphore.Unix.cpp +++ b/Source/Threading/Primitives/AuSemaphore.Unix.cpp @@ -63,8 +63,9 @@ namespace Aurora::Threading::Primitives else { struct timespec tspec; - Time::ms2tsabs(&tspec, timeout); + Time::ms2tsabsRealtime(&tspec, timeout); + // int ret {}; do diff --git a/Source/Time/Clock.cpp b/Source/Time/Clock.cpp index 19d6ec31..07dc8a49 100644 --- a/Source/Time/Clock.cpp +++ b/Source/Time/Clock.cpp @@ -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 @@ -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(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(spec.tv_sec) + AuNSToMS(spec.tv_nsec); + } + #endif + + #if defined(AURORA_IS_MODERNNT_DERIVED) + return std::chrono::duration_cast(high_res_clock::now().time_since_epoch()).count(); + #endif + + return std::chrono::duration_cast(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(AuSToMS(spec.tv_sec)) + (AuUInt64)spec.tv_nsec; + } + #endif + + #if defined(AURORA_IS_MODERNNT_DERIVED) + return std::chrono::duration_cast(high_res_clock::now().time_since_epoch()).count(); + #endif + + return std::chrono::duration_cast(steady_clock::now().time_since_epoch()).count(); + } + AUKN_SYM AuInt64 CTimeToMS(time_t time) { return std::chrono::duration_cast(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(high_res_clock::period::den) / static_cast(high_res_clock::period::num); } diff --git a/Source/Time/Time.hpp b/Source/Time/Time.hpp index b30b5377..5e58d1d3 100644 --- a/Source/Time/Time.hpp +++ b/Source/Time/Time.hpp @@ -54,7 +54,7 @@ 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; @@ -63,6 +63,17 @@ namespace Aurora::Time 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) { auto baseNS = ns + AuMSToNS(999'080'100'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;