From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001
From: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Date: Sat, 8 Apr 2023 01:22:00 +0200
Subject: Move solution and projects to src
---
.../Memory/PartialUnmaps/PartialUnmapState.cs | 163 +++++++++++++++++++++
1 file changed, 163 insertions(+)
create mode 100644 src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs
(limited to 'src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs')
diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs
new file mode 100644
index 00000000..5b0bc07e
--- /dev/null
+++ b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
+using System.Runtime.Versioning;
+using System.Threading;
+
+using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers;
+
+namespace Ryujinx.Common.Memory.PartialUnmaps
+{
+ ///
+ /// State for partial unmaps. Intended to be used on Windows.
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public partial struct PartialUnmapState
+ {
+ public NativeReaderWriterLock PartialUnmapLock;
+ public int PartialUnmapsCount;
+ public ThreadLocalMap LocalCounts;
+
+ public readonly static int PartialUnmapLockOffset;
+ public readonly static int PartialUnmapsCountOffset;
+ public readonly static int LocalCountsOffset;
+
+ public readonly static IntPtr GlobalState;
+
+ [SupportedOSPlatform("windows")]
+ [LibraryImport("kernel32.dll")]
+ private static partial int GetCurrentThreadId();
+
+ [SupportedOSPlatform("windows")]
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ private static partial IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId);
+
+ [SupportedOSPlatform("windows")]
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ private static partial bool CloseHandle(IntPtr hObject);
+
+ [SupportedOSPlatform("windows")]
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static partial bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
+
+ ///
+ /// Creates a global static PartialUnmapState and populates the field offsets.
+ ///
+ static unsafe PartialUnmapState()
+ {
+ PartialUnmapState instance = new PartialUnmapState();
+
+ PartialUnmapLockOffset = OffsetOf(ref instance, ref instance.PartialUnmapLock);
+ PartialUnmapsCountOffset = OffsetOf(ref instance, ref instance.PartialUnmapsCount);
+ LocalCountsOffset = OffsetOf(ref instance, ref instance.LocalCounts);
+
+ int size = Unsafe.SizeOf();
+ GlobalState = Marshal.AllocHGlobal(size);
+ Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size);
+ }
+
+ ///
+ /// Resets the global state.
+ ///
+ public static unsafe void Reset()
+ {
+ int size = Unsafe.SizeOf();
+ Unsafe.InitBlockUnaligned((void*)GlobalState, 0, (uint)size);
+ }
+
+ ///
+ /// Gets a reference to the global state.
+ ///
+ /// A reference to the global state
+ public static unsafe ref PartialUnmapState GetRef()
+ {
+ return ref Unsafe.AsRef((void*)GlobalState);
+ }
+
+ ///
+ /// Checks if an access violation handler should retry execution due to a fault caused by partial unmap.
+ ///
+ ///
+ /// Due to Windows limitations, might need to unmap more memory than requested.
+ /// The additional memory that was unmapped is later remapped, however this leaves a time gap where the
+ /// memory might be accessed but is unmapped. Users of the API must compensate for that by catching the
+ /// access violation and retrying if it happened between the unmap and remap operation.
+ /// This method can be used to decide if retrying in such cases is necessary or not.
+ ///
+ /// This version of the function is not used, but serves as a reference for the native
+ /// implementation in ARMeilleure.
+ ///
+ /// True if execution should be retried, false otherwise
+ [SupportedOSPlatform("windows")]
+ public bool RetryFromAccessViolation()
+ {
+ PartialUnmapLock.AcquireReaderLock();
+
+ int threadID = GetCurrentThreadId();
+ int threadIndex = LocalCounts.GetOrReserve(threadID, 0);
+
+ if (threadIndex == -1)
+ {
+ // Out of thread local space... try again later.
+
+ PartialUnmapLock.ReleaseReaderLock();
+
+ return true;
+ }
+
+ ref int threadLocalPartialUnmapsCount = ref LocalCounts.GetValue(threadIndex);
+
+ bool retry = threadLocalPartialUnmapsCount != PartialUnmapsCount;
+ if (retry)
+ {
+ threadLocalPartialUnmapsCount = PartialUnmapsCount;
+ }
+
+ PartialUnmapLock.ReleaseReaderLock();
+
+ return retry;
+ }
+
+ ///
+ /// Iterates and trims threads in the thread -> count map that
+ /// are no longer active.
+ ///
+ [SupportedOSPlatform("windows")]
+ public void TrimThreads()
+ {
+ const uint ExitCodeStillActive = 259;
+ const int ThreadQueryInformation = 0x40;
+
+ Span ids = LocalCounts.ThreadIds.AsSpan();
+
+ for (int i = 0; i < ids.Length; i++)
+ {
+ int id = ids[i];
+
+ if (id != 0)
+ {
+ IntPtr handle = OpenThread(ThreadQueryInformation, false, (uint)id);
+
+ if (handle == IntPtr.Zero)
+ {
+ Interlocked.CompareExchange(ref ids[i], 0, id);
+ }
+ else
+ {
+ GetExitCodeThread(handle, out uint exitCode);
+
+ if (exitCode != ExitCodeStillActive)
+ {
+ Interlocked.CompareExchange(ref ids[i], 0, id);
+ }
+
+ CloseHandle(handle);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--
cgit v1.2.3