From 4770e85573f0b553c2f0efead5a7e325b4e63bec Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:20:51 +0300 Subject: [PATCH] Cross-platform atomic operations. (#44) * Cross-platform spin lock implementation. * Cross-platform reference counting. --- .../Base/Type/detail/hhStringHolder.inl | 8 ++++-- .../api/boost/smart_ptr/shared_ptr.h | 12 ++++++--- UnleashedRecomp/gpu/video.h | 8 ++++-- UnleashedRecomp/kernel/imports.cpp | 26 ++++++++++++++----- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl index 5bd47552..521a21d1 100644 --- a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl +++ b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.inl @@ -44,22 +44,26 @@ namespace Hedgehog::Base inline void SStringHolder::AddRef() { + std::atomic_ref atomicRef(RefCountAndLength.value); + uint32_t originalValue, incrementedValue; do { originalValue = RefCountAndLength.value; incrementedValue = ByteSwap(ByteSwap(originalValue) + 1); - } while (InterlockedCompareExchange(reinterpret_cast(&RefCountAndLength), incrementedValue, originalValue) != originalValue); + } while (!atomicRef.compare_exchange_weak(originalValue, incrementedValue)); } inline void SStringHolder::Release() { + std::atomic_ref atomicRef(RefCountAndLength.value); + uint32_t originalValue, decrementedValue; do { originalValue = RefCountAndLength.value; decrementedValue = ByteSwap(ByteSwap(originalValue) - 1); - } while (InterlockedCompareExchange(reinterpret_cast(&RefCountAndLength), decrementedValue, originalValue) != originalValue); + } while (!atomicRef.compare_exchange_weak(originalValue, decrementedValue)); if (RefCountAndLength == 0) __HH_FREE(this); diff --git a/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h b/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h index adec62eb..9e62fdbb 100644 --- a/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h +++ b/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h @@ -28,22 +28,26 @@ namespace boost void add_ref() { + std::atomic_ref useCount(use_count_.value); + be original, incremented; do { original = use_count_; incremented = original + 1; - } while (InterlockedCompareExchange((unsigned long*)&use_count_, incremented.value, original.value) != original.value); + } while (!useCount.compare_exchange_weak(original.value, incremented.value)); } void release() { + std::atomic_ref useCount(use_count_.value); + be original, decremented; do { original = use_count_; decremented = original - 1; - } while (InterlockedCompareExchange((unsigned long*)&use_count_, decremented.value, original.value) != original.value); + } while (!useCount.compare_exchange_weak(original.value, decremented.value)); if (decremented == 0) { @@ -54,12 +58,14 @@ namespace boost void weak_release() { + std::atomic_ref weakCount(weak_count_.value); + be original, decremented; do { original = weak_count_; decremented = original - 1; - } while (InterlockedCompareExchange((unsigned long*)&weak_count_, decremented.value, original.value) != original.value); + } while (!weakCount.compare_exchange_weak(original.value, decremented.value)); if (decremented == 0) { diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index 34ddf886..7dd293c7 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -84,22 +84,26 @@ struct GuestResource void AddRef() { + std::atomic_ref atomicRef(refCount.value); + uint32_t originalValue, incrementedValue; do { originalValue = refCount.value; incrementedValue = ByteSwap(ByteSwap(originalValue) + 1); - } while (InterlockedCompareExchange(reinterpret_cast(&refCount), incrementedValue, originalValue) != originalValue); + } while (!atomicRef.compare_exchange_weak(originalValue, incrementedValue)); } void Release() { + std::atomic_ref atomicRef(refCount.value); + uint32_t originalValue, decrementedValue; do { originalValue = refCount.value; decrementedValue = ByteSwap(ByteSwap(originalValue) - 1); - } while (InterlockedCompareExchange(reinterpret_cast(&refCount), decrementedValue, originalValue) != originalValue); + } while (!atomicRef.compare_exchange_weak(originalValue, decrementedValue)); // Normally we are supposed to release here, so only use this // function when you know you won't be the one destructing it. diff --git a/UnleashedRecomp/kernel/imports.cpp b/UnleashedRecomp/kernel/imports.cpp index ebbf7e4e..d1d02103 100644 --- a/UnleashedRecomp/kernel/imports.cpp +++ b/UnleashedRecomp/kernel/imports.cpp @@ -695,15 +695,22 @@ void RtlRaiseException_x() void KfReleaseSpinLock(uint32_t* spinLock) { - InterlockedExchange((volatile long*)spinLock, 0); + std::atomic_ref spinLockRef(*spinLock); + spinLockRef = 0; } void KfAcquireSpinLock(uint32_t* spinLock) { - const auto ctx = GetPPCContext(); + std::atomic_ref spinLockRef(*spinLock); + + while (true) + { + uint32_t expected = 0; + if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32)) + break; - while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0) std::this_thread::yield(); + } } uint64_t KeQueryPerformanceFrequency() @@ -735,15 +742,22 @@ void VdGetSystemCommandBuffer() void KeReleaseSpinLockFromRaisedIrql(uint32_t* spinLock) { - InterlockedExchange((volatile long*)spinLock, 0); + std::atomic_ref spinLockRef(*spinLock); + spinLockRef = 0; } void KeAcquireSpinLockAtRaisedIrql(uint32_t* spinLock) { - const auto ctx = GetPPCContext(); + std::atomic_ref spinLockRef(*spinLock); + + while (true) + { + uint32_t expected = 0; + if (spinLockRef.compare_exchange_weak(expected, g_ppcContext->r13.u32)) + break; - while (InterlockedCompareExchange((volatile long*)spinLock, ByteSwap(*(uint32_t*)(g_memory.Translate(ctx->r13.u32 + 0x110))), 0) != 0) std::this_thread::yield(); + } } uint32_t KiApcNormalRoutineNop()