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