AuroraRuntime/Source/Grug/Grug.cpp

188 lines
4.9 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Grug.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 <Source/RuntimeInternal.hpp>
#include "Grug.hpp"
#include <Source/Logging/Logger.hpp>
#include <Source/Console/ConsoleFIO/ConsoleFIO.hpp>
#include <Source/RuntimeInternal.hpp>
#include <Source/Console/Flusher.hpp>
namespace Aurora::Grug
{
static const auto kGrugSleepMs = 100;
static const auto kGrugFlushMs = 500;
static AuThreads::ThreadUnique_t gGrugsBigWorld;
static AuThreadPrimitives::ConditionVariableUnique_t gCondVar; // slow logger work queue
static AuThreadPrimitives::ConditionMutexUnique_t gMutex; // ^ that
static AuThreadPrimitives::SemaphoreUnique_t gArrows;
static void SlowStartupTasks()
{
Console::ConsoleFIO::FIOCleanup();
}
// grug require only 1 strand
static void GrugWorld()
{
// grug surive first night
SlowStartupTasks();
AU_LOCK_GUARD(gMutex);
Utility::RateLimiter limiter;
limiter.SetNextStep(kGrugFlushMs);
// grug has cave
while (AuIsThreadRunning())
{
// * -> grug wake up from 100ms (kGrugSleepMs)
// grug smashy alarm
gCondVar->WaitForSignal(kGrugFlushMs);
// grug anoy
if (gArrows && gArrows->TryLock())
{
// grug yeet
DequeueOneArrow();
}
// grug pump all async log messages
GrugFlushWrites();
// adhd grug wonder around the log pool every 500ms (rate limited to 100ms sleeps)
if (limiter.CheckExchangePass())
{
GrugFlushFlushs();
}
// grug give up
// grug yield for at least 100ms -> *
if (gArrows)
{
// grug sleep for 100ms or until poked
if (gArrows->Lock(kGrugSleepMs))
{
DequeueOneArrow();
}
}
else
{
// grug sleep 100ms
AuThreading::Sleep(kGrugSleepMs);
}
}
}
static void DestroyFlushThread()
{
gGrugsBigWorld.reset();
}
static void InitFlushThread()
{
// Startup a runner thread that will take care of all the stress of triggering IO every so often on a remote thread
gGrugsBigWorld = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(GrugWorld)),
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"Cave Grug"
));
if (!gGrugsBigWorld)
{
return;
}
// he's a lazy bastard (ira type insult)
gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow);
gGrugsBigWorld->Run();
}
void InitGrug()
{
gMutex = AuThreadPrimitives::ConditionMutexUnique();
SysAssert(gMutex, "Couldn't allocate a unique condition variable mutex for grug");
auto shared = AuUnsafeRaiiToShared(gMutex);
gCondVar = AuThreadPrimitives::ConditionVariableUnique(shared);
SysAssert(gCondVar, "Couldn't allocate a unique condition variable for grug");
gArrows = AuThreadPrimitives::SemaphoreUnique();
SysAssert(gArrows, "Couldn't allocate an arrow counter for grug");
InitFlushThread();
}
void DeinitGrug()
{
gGrugsBigWorld.reset();
GrugFlushWrites();
GrugFlushFlushs();
gMutex.reset();
gCondVar.reset();
gArrows.reset();
}
void NotifyGrugOfTelemetry()
{
if (gArrows)
{
gArrows->Unlock(1);
}
DrachenlordScreech();
}
void DrachenlordScreech()
{
if (!gCondVar)
{
return;
}
gCondVar->Signal();
}
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 GrugFlushFlushs()
{
Logging::ForceFlushFlush();
Console::ForceFlush();
}
}