/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuGrug.cpp Date: 2022-01-03 File: Flusher.cpp Date: 2021-8-27 Author: Reece Note: Grug couples async telemetry, console flushing, and log dispatching to one lazy working thread w/ high performance thread primitives for when urgent telemetry events are raised. ***/ #include #include "AuGrug.hpp" #include #include #include #include #include #include #if defined(AURORA_IS_LINUX_DERIVED) void LinuxSuperSecretIOTick(); void LinuxSuperSecretFuckGlibc(); #endif namespace Aurora::Grug { static const auto kGrugSleepMs = 300; static const auto kGrugFlushMs = 600; static AuThreads::ThreadUnique_t gGrugsBigWorld; static AuSemaphore gArrows; static AuBinarySemaphore gArrowsRetarded(false, true, true); static AuMutex gOtherMutex; static AuList> gHandlesToClose; static AuList gEventsToTrigger; static void SlowStartupTasks() { Console::ConsoleFIO::FIOCleanup(); } // grug require only 1 strand static void GrugWorld() { // ignorerino #if defined(AURORA_IS_LINUX_DERIVED) ::LinuxSuperSecretFuckGlibc(); #endif // grug surive first night SlowStartupTasks(); // this thread must never encounter an out of memory condition. begin out of memory mitigations. AuDebug::AddMemoryCrunch(); Utility::RateLimiter limiter; limiter.SetNextStep(AuMSToNS(kGrugFlushMs)); // grug has cave while (AuIsThreadRunning()) { // grug anoyd // grug pump all async log messages GrugFlushWrites(); // adhd grug wonder around the log pool every 500ms (rate limited to 100ms sleeps) if (limiter.CheckExchangePass()) { // poo2loo GrugFlushFlushs(); } // grug give up // grug sleep for 100ms or until poked if (gArrowsRetarded->TryLock() || gArrows->LockMS(kGrugSleepMs)) { DequeueOneArrow(); } // grug has to carry stupid platforms #if defined(AURORA_IS_LINUX_DERIVED) ::LinuxSuperSecretIOTick(); #endif // grug done GrugDoIoWork(); } } static void DestroyFlushThread() { gGrugsBigWorld.reset(); } static void InitFlushThread() { if (gGrugsBigWorld) { return; } // Startup a runner thread that will take care of all the stress of triggering IO every so often on a remote thread gGrugsBigWorld = AuMove(AuThreads::Spawn(std::bind(GrugWorld), false, {}, "Cave Grug" )); if (!gGrugsBigWorld) { return; } // he's a lazy bastard gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient); gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow); } void InitGrug() { InitFlushThread(); } void DeinitGrug() { if (bool(gGrugsBigWorld) && gGrugsBigWorld.get() != AuThreads::GetThread()) { gGrugsBigWorld.reset(); } GrugFlushWrites(); GrugFlushFlushs(); DeinitArrows(); } void NotifyGrugOfTelemetry() { gArrows->Unlock(1); } void DrachenlordScreech() { gArrowsRetarded->Set(); } bool IsGrug() { if (!gGrugsBigWorld) return false; return AuThreads::GetThread() == gGrugsBigWorld.get(); } void NotifyGrugOfLogs() { if (Grug::IsGrug()) { Grug::GrugFlushWrites(); } else { Grug::DrachenlordScreech(); } } void GrugFlushWrites() { Logging::ForceFlushLoggers(); } void GrugDoIoWork() { decltype(gHandlesToClose) toClose; decltype(gEventsToTrigger) toTrigger; if (gHandlesToClose.empty() && gEventsToTrigger.empty()) { return; } { AU_LOCK_GLOBAL_GUARD(gOtherMutex); toClose = AuMove(gHandlesToClose); toTrigger = AuMove(gEventsToTrigger); } for (const auto [uHandle, bFlush, bWriteEoS] : toClose) { if (!SysHandleIsNonZero(uHandle)) { continue; } if (bWriteEoS) { SysWriteEoS(uHandle); } if (bFlush) { #if defined(AURORA_IS_MODERNNT_DERIVED) if (AuIO::IsHandleFile(uHandle)) #endif { SysFlushHandle(uHandle); } } SysCloseHandle(uHandle); } for (const auto pEvent : toTrigger) { pEvent->Set(); } } void GrugFlushFlushs() { Logging::ForceFlushFlush(); Console::PumpOffMain(); Console::ForceFlush(); GrugDoIoWork(); } void CloseHandle(AuUInt64 handle, bool bFlush, bool bWriteEoS) { { AU_DEBUG_MEMCRUNCH; AU_LOCK_GLOBAL_GUARD(gOtherMutex); gHandlesToClose.push_back(AuMakeTuple(AuUInt(handle), bFlush, bWriteEoS)); } NotifyGrugOfTelemetry(); } void WaitForGrugTick() { AuEvent event(false, true, true); if (gGrugsBigWorld && gGrugsBigWorld.get() == AuThreads::GetThread()) { return; } { AU_DEBUG_MEMCRUNCH; AU_LOCK_GLOBAL_GUARD(gOtherMutex); gEventsToTrigger.push_back(event.AsPointer()); } { NotifyGrugOfTelemetry(); } event->Lock(); } }