/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Exit.cpp Date: 2022-2-5 Author: Reece ***/ #include #include "Exit.hpp" #include #include "MTWatchDog.hpp" #if defined(AURORA_IS_POSIX_DERIVED) #include "Exit.Unix.hpp" #endif namespace Aurora::Exit { static AuThreadPrimitives::MutexUnique_t gMutex; static AuList, 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 &callback) { AU_LOCK_GUARD(gMutex); return AuTryInsert(gTriggerSubscribers, AuMakePair(callback, level)); } AUKN_SYM void ExitHandlerRemove(const AuSPtr &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(); } }