From e02be8073e27fdbddc9df338119342cf25de9315 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 7 Mar 2023 15:04:18 -0800 Subject: [PATCH] FEXCore: Support deferred signal mutex This is part of FEXCore since it pulls in InternalThreadData, but is related to the FHU signal mutex class. Necessary to allow deferring signals in C++ code rather than right in the JIT. --- .../FEXCore/Utils/DeferredSignalMutex.h | 134 ++++++++++++++++++ .../FEXHeaderUtils/ScopedSignalMask.h | 2 +- 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 External/FEXCore/include/FEXCore/Utils/DeferredSignalMutex.h diff --git a/External/FEXCore/include/FEXCore/Utils/DeferredSignalMutex.h b/External/FEXCore/include/FEXCore/Utils/DeferredSignalMutex.h new file mode 100644 index 000000000..e0293bd03 --- /dev/null +++ b/External/FEXCore/include/FEXCore/Utils/DeferredSignalMutex.h @@ -0,0 +1,134 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace FEXCore { + + template + class ScopedDeferredSignalWithMutexBase final { + public: + + ScopedDeferredSignalWithMutexBase(MutexType &_Mutex, FEXCore::Core::InternalThreadState *Thread) + : Mutex {&_Mutex} + , Thread {Thread} { + // Needs to be atomic so that operations can't end up getting reordered around this. + Thread->CurrentFrame->State.DeferredSignalRefCount.Increment(1); + // Lock the mutex + (Mutex->*lock_fn)(); + } + + // No copy or assignment possible + ScopedDeferredSignalWithMutexBase(const ScopedDeferredSignalWithMutexBase&) = delete; + ScopedDeferredSignalWithMutexBase& operator=(ScopedDeferredSignalWithMutexBase&) = delete; + + // Only move + ScopedDeferredSignalWithMutexBase(ScopedDeferredSignalWithMutexBase &&rhs) + : Mutex {rhs.Mutex} + , Thread {rhs.Thread} { + rhs.Mutex = nullptr; + } + + ~ScopedDeferredSignalWithMutexBase() { + if (Mutex != nullptr) { + // Unlock the mutex + (Mutex->*unlock_fn)(); + +#ifdef _M_X86_64 + // Needs to be atomic so that operations can't end up getting reordered around this. + // Without this, the recount and the signal access could get reordered. + auto Result = Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1); + + // X86-64 must do an additional check around the store. + if ((Result - 1) == 0) { + // Must happen after the refcount store + Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0); + } +#else + Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1); + Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0); +#endif + } + } + private: + MutexType *Mutex; + FEXCore::Core::InternalThreadState *Thread; + }; + + using ScopedDeferredSignalWithMutex = ScopedDeferredSignalWithMutexBase; + using ScopedDeferredSignalWithSharedLock = ScopedDeferredSignalWithMutexBase; + using ScopedDeferredSignalWithUniqueLock = ScopedDeferredSignalWithMutexBase; + + template + class ScopedPotentialDeferredSignalWithMutexBase final { + public: + + ScopedPotentialDeferredSignalWithMutexBase(MutexType &_Mutex, FEXCore::Core::InternalThreadState *Thread, uint64_t Mask = ~0ULL) + : Mutex {&_Mutex} + , Thread {Thread} { + if (Thread) { + Thread->CurrentFrame->State.DeferredSignalRefCount.Increment(1); + } + else { + // Mask all signals, storing the original incoming mask + ::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &Mask, &OriginalMask, sizeof(OriginalMask)); + } + // Lock the mutex + (Mutex->*lock_fn)(); + } + + // No copy or assignment possible + ScopedPotentialDeferredSignalWithMutexBase(const ScopedPotentialDeferredSignalWithMutexBase&) = delete; + ScopedPotentialDeferredSignalWithMutexBase& operator=(ScopedPotentialDeferredSignalWithMutexBase&) = delete; + + // Only move + ScopedPotentialDeferredSignalWithMutexBase(ScopedPotentialDeferredSignalWithMutexBase &&rhs) + : Mutex {rhs.Mutex} + , Thread {rhs.Thread} { + rhs.Mutex = nullptr; + } + + ~ScopedPotentialDeferredSignalWithMutexBase() { + if (Mutex != nullptr) { + // Unlock the mutex + (Mutex->*unlock_fn)(); + + if (Thread) { +#ifdef _M_X86_64 + // Needs to be atomic so that operations can't end up getting reordered around this. + // Without this, the recount and the signal access could get reordered. + auto Result = Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1); + + // X86-64 must do an additional check around the store. + if ((Result - 1) == 0) { + // Must happen after the refcount store + Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0); + } +#else + Thread->CurrentFrame->State.DeferredSignalRefCount.Decrement(1); + Thread->CurrentFrame->State.DeferredSignalFaultAddress->Store(0); +#endif + } + else { + // Unmask back to the original signal mask + ::syscall(SYS_rt_sigprocmask, SIG_SETMASK, &OriginalMask, nullptr, sizeof(OriginalMask)); + } + } + } + private: + MutexType *Mutex; + uint64_t OriginalMask{}; + FEXCore::Core::InternalThreadState *Thread; + }; + + using ScopedPotentialDeferredSignalWithMutex = ScopedPotentialDeferredSignalWithMutexBase; + using ScopedPotentialDeferredSignalWithSharedLock = ScopedPotentialDeferredSignalWithMutexBase; + using ScopedPotentialDeferredSignalWithUniqueLock = ScopedPotentialDeferredSignalWithMutexBase; +} diff --git a/FEXHeaderUtils/FEXHeaderUtils/ScopedSignalMask.h b/FEXHeaderUtils/FEXHeaderUtils/ScopedSignalMask.h index 0996b4b8b..cb393ad94 100644 --- a/FEXHeaderUtils/FEXHeaderUtils/ScopedSignalMask.h +++ b/FEXHeaderUtils/FEXHeaderUtils/ScopedSignalMask.h @@ -1,8 +1,8 @@ #pragma once #include -#include +#include #include #include #include