AuroraRuntime/Source/Exit/Exit.cpp

169 lines
4.0 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Exit.cpp
Date: 2022-2-5
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Exit.hpp"
#include <Source/Grug/Grug.hpp>
#include "MTWatchDog.hpp"
#if defined(AURORA_IS_POSIX_DERIVED)
#include "Exit.Unix.hpp"
#endif
namespace Aurora::Exit
{
static AuThreadPrimitives::MutexUnique_t gMutex;
static AuList<AuTuple<AuSPtr<IExitSubscriber>, ETriggerLevel>> gTriggerSubscribers;
static bool gIsAppRunning {true};
static void DispatchHandlersForThread(AuThreads::IAuroraThread *pThread, ETriggerLevel level)
{
ExitInvoker invoker;
invoker.pCaller = pThread;
for (const auto &[sub, subLevel] : gTriggerSubscribers)
{
if (level == subLevel)
{
try
{
sub->OnTrigger(level, &invoker);
}
catch (...)
{
// Intentionally left empty. Telemetry hooks will report, if possible.
}
}
}
}
void PostLevel(AuThreads::IAuroraThread *thread, ETriggerLevel level)
{
bool bOldTerminatingValue;
{
AU_LOCK_GUARD(gMutex);
bool isTerminate = level == ETriggerLevel::eSafeTermination;
bool isPreempting {};
// Prevent double eSafeTermination
if (isTerminate && gHasSentTerminate)
{
isPreempting = true;
}
//
bOldTerminatingValue = gIsAppRunning;
if (isTerminate || level == ETriggerLevel::eSigTerminate)
{
gIsAppRunning = false;
}
static AuUInt32 gProblemCounter = {};
// Mitigate reused stack, nested try/catch, spam
if (level == ETriggerLevel::eProblematicEvent)
{
if (AuAtomicTestAndSet(&gProblemCounter, 1))
{
return;
}
}
// HACK:
gHasCanceled = false;
// Dispatch
DispatchHandlersForThread(thread, level);
// ...
if (level == ETriggerLevel::eProblematicEvent)
{
Grug::NotifyGrugOfLogs();
}
else
{
Grug::GrugFlushWrites();
Grug::GrugFlushFlushs();
}
gProblemCounter = 0;
// Has already sent eSafeTermination?
if (isPreempting)
{
return;
}
// ...
gHasSentTerminate |= isTerminate;
}
// Force exit after calling the subscribers, should the level be eSigTerminate
if (level == ETriggerLevel::eSigTerminate)
{
// HACK:
if (gHasCanceled)
{
gIsAppRunning = bOldTerminatingValue;
}
else
{
Process::Exit(0);
}
}
}
AUKN_SYM bool ExitHandlerAdd(ETriggerLevel level, const AuSPtr<IExitSubscriber> &callback)
{
AU_LOCK_GUARD(gMutex);
return AuTryInsert(gTriggerSubscribers, AuMakePair(callback, level));
}
AUKN_SYM void ExitHandlerRemove(const AuSPtr<IExitSubscriber> &callback)
{
AU_LOCK_GUARD(gMutex);
AuRemoveAllIf(gTriggerSubscribers, [=](const auto &entry) -> bool
{
return AuGet<0>(entry) == callback;
});
}
AUKN_SYM bool IsAppRunning()
{
return gIsAppRunning;
}
AUKN_SYM void CancelExit()
{
// HACK:
gHasCanceled = true;
}
void InitExit()
{
gMutex = AuThreadPrimitives::MutexUnique();
InitWatchdog();
#if defined(AURORA_IS_POSIX_DERIVED)
InitUnix();
#endif
}
void DeinitExit()
{
#if defined(AURORA_IS_POSIX_DERIVED)
DeinitUnix();
#endif
gMutex.reset();
}
}