/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuArrow.cpp Date: 2022-02-03 Author: Reece Note: NIX signal aware telemetry IPC to cave grug ***/ #include #include "AuGrug.hpp" #include "AuArrow.hpp" #include namespace Aurora::Grug { static AuList gReservedArrows; // stacks are slower than vectors under msvc, 'twas a qst bottleneck static AuThreadPrimitives::SpinLock gSpinLock; static bool gDisableGrugMessages {}; void SignalSafeSleepLockYield(AuThreading::IWaitable *pLock) { #if defined(AURORA_IS_POSIX_DERIVED) while (!pLock->TryLock()) { sched_yield(); } #else pLock->Lock(); #endif } void HurlArrow(Arrow *pArrow, GrugReport_f pCallback, GrugReport_f pCallbackRunaway) { if (gDisableGrugMessages) { return; } pArrow->pCallback = pCallback; pArrow->pCallbackRunaway = pCallbackRunaway; #if !defined(ARROW_NO_MUTEX) SignalSafeSleepLockYield(pArrow->spinSemaphore2.AsPointer()); #else SignalSafeSleepLockYield(&pArrow->spinSemaphore); #endif { SignalSafeSleepLockYield(&gSpinLock); while (!AuTryInsert(gReservedArrows, pArrow)) { #if defined(AURORA_IS_POSIX_DERIVED) sched_yield(); #else AuThreading::ContextYield(); #endif } gSpinLock.Unlock(); } NotifyGrugOfTelemetry(); } void ArrowWait(Arrow *arrow) { if (gDisableGrugMessages) { return; } #if defined(ARROW_NO_MUTEX) AU_LOCK_GUARD(arrow->spinSemaphore); #else AU_LOCK_GUARD(arrow->spinSemaphore2); #endif } void ArrowThreadAsyncSafe(Arrow *pArrow) { #if defined(ARROW_NO_MUTEX) SignalSafeSleepLockYield(&pArrow->spinSemaphore); pArrow->spinSemaphore.Unlock(); #else SignalSafeSleepLockYield(pArrow->spinSemaphore2.AsPointer()); pArrow->spinSemaphore2->Unlock(); #endif } void DequeueOneArrow() { AU_LOCK_GUARD(gSpinLock); if (gReservedArrows.empty()) { return; } auto last = *(gReservedArrows.end() - 1); if (last->pCallback) { try { last->pCallback(last); } catch (...) { } } auto lastCallback = last->pCallbackRunaway; last->spinSemaphore.Unlock(); #if !defined(ARROW_NO_MUTEX) last->spinSemaphore2->Unlock(); #endif gReservedArrows.pop_back(); if (lastCallback) { try { lastCallback(last); } catch (...) { } } } static void HandleProblematicEvent(Arrow *pArrow) { Exit::PostLevel(pArrow->thread.ToThread(), Exit::ETriggerLevel::eProblematicEvent); } void HurlRaiseProblematicEvent(Arrow *pArrow) { HurlArrow(pArrow, {}, HandleProblematicEvent); ArrowWait(pArrow); // protect the stack, dont trust the caller } static void HandleFatal(Arrow *pArrow) { Exit::PostLevel(pArrow->thread.ToThread(), Exit::ETriggerLevel::eFatalException); } void HurlFatalEvent(Arrow *pArrow) { HurlArrow(pArrow, HandleFatal, {}); ArrowWait(pArrow); // wait until the fatal error has been handled } void InitArrows() { gReservedArrows.reserve(20); } void DeinitArrows() { gDisableGrugMessages = true; } }