169 lines
4.0 KiB
C++
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();
|
|
}
|
|
} |