From 8324faed84b924a22d40df34f56431e1712da7c2 Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Fri, 2 Oct 2026 18:48:24 +0300 Subject: [PATCH] [HLE] Resolve backing aliases for readers that hold an access window SharedBackingViews publishes its view snapshot as an alias identity, and a reader can enter an alias access window once and resolve guest pages to their backing alias without the views lock until the snapshot changes. --- .../Memory/PhysicalVirtualMemory.cs | 12 +++++++ .../GuestMemory/GuestSpaceOwner.cs | 8 +++++ .../GuestMemory/SharedBackingViews.cs | 36 +++++++++++++++++-- src/SharpEmu.HLE/IGuestBackedSpace.cs | 14 ++++++++ .../GuestMemory/SharedBackingTransferTests.cs | 25 +++++++++++++ 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs index a395da1..fe3b026 100644 --- a/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs +++ b/src/SharpEmu.Core/Memory/PhysicalVirtualMemory.cs @@ -1174,6 +1174,18 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA } } + public object? BackingAliasSnapshot => _backedSpace?.AliasSnapshot; + + public bool TryEnterBackingAliasAccess() => !_disposed && _backedSpace is { } backed && backed.TryEnterAliasAccess(); + + public void ExitBackingAliasAccess() => _backedSpace?.ExitAliasAccess(); + + public bool TryResolveBackingAlias(ulong address, ulong size, out ulong alias) + { + alias = 0; + return !_disposed && _backedSpace is { } backed && backed.TryResolveAlias(address, size, out alias); + } + private bool HasBackingOwner() => !_disposed && _backedSpace != null; // A span may cross adjacent view records; the owner validates full coverage. diff --git a/src/SharpEmu.HLE/GuestMemory/GuestSpaceOwner.cs b/src/SharpEmu.HLE/GuestMemory/GuestSpaceOwner.cs index 107aac7..2b44b28 100644 --- a/src/SharpEmu.HLE/GuestMemory/GuestSpaceOwner.cs +++ b/src/SharpEmu.HLE/GuestMemory/GuestSpaceOwner.cs @@ -67,6 +67,14 @@ public sealed class GuestSpaceOwner : IDisposable // Reads through a view that covers the whole range without any lock; false otherwise. public bool TryReadSingleView(ulong address, Span data) => _views.TryReadSingleView(address, data); + public object AliasSnapshot => _views.AliasSnapshot; + + public bool TryEnterAliasAccess() => _views.TryEnterAliasAccess(); + + public void ExitAliasAccess() => _views.ExitAliasAccess(); + + public bool TryResolveAlias(ulong address, ulong size, out ulong alias) => _views.TryResolveAlias(address, size, out alias); + public bool TryCopyBacking(ulong destination, ulong source, ulong size) => _views.TryCopyBacking(destination, source, size); public bool IsBacked(ulong address, ulong size) => _views.Contains(address, size); diff --git a/src/SharpEmu.HLE/GuestMemory/SharedBackingViews.cs b/src/SharpEmu.HLE/GuestMemory/SharedBackingViews.cs index f52f13a..43be043 100644 --- a/src/SharpEmu.HLE/GuestMemory/SharedBackingViews.cs +++ b/src/SharpEmu.HLE/GuestMemory/SharedBackingViews.cs @@ -623,7 +623,40 @@ public sealed unsafe class SharedBackingViews : IDisposable } Interlocked.Increment(ref _activeAccesses); - var snapshot = Volatile.Read(ref _snapshot); + if (TryFindAlias(Volatile.Read(ref _snapshot), address, size, out target)) + { + return true; + } + + Interlocked.Decrement(ref _activeAccesses); + return false; + } + + public object AliasSnapshot => Volatile.Read(ref _snapshot); + + public bool TryEnterAliasAccess() + { + Interlocked.Increment(ref _activeAccesses); + if (!Volatile.Read(ref _disposed) && _backing != null) + { + return true; + } + + Interlocked.Decrement(ref _activeAccesses); + return false; + } + + public void ExitAliasAccess() => Interlocked.Decrement(ref _activeAccesses); + + public bool TryResolveAlias(ulong address, ulong size, out ulong target) + { + target = 0; + return size != 0 && ulong.MaxValue - address >= size && TryFindAlias(Volatile.Read(ref _snapshot), address, size, out target); + } + + private bool TryFindAlias(ViewRecord[] snapshot, ulong address, ulong size, out ulong target) + { + target = 0; var low = 0; var high = snapshot.Length - 1; var found = -1; @@ -655,7 +688,6 @@ public sealed unsafe class SharedBackingViews : IDisposable } } - Interlocked.Decrement(ref _activeAccesses); return false; } diff --git a/src/SharpEmu.HLE/IGuestBackedSpace.cs b/src/SharpEmu.HLE/IGuestBackedSpace.cs index 6a37ed4..8d21777 100644 --- a/src/SharpEmu.HLE/IGuestBackedSpace.cs +++ b/src/SharpEmu.HLE/IGuestBackedSpace.cs @@ -24,4 +24,18 @@ public interface IGuestBackedSpace // Copies through the backing alias only: no protection change, no store notification. bool TryWriteBacking(ulong address, ReadOnlySpan data); bool TryReadBacking(ulong address, Span data); + + object? BackingAliasSnapshot => null; + + bool TryEnterBackingAliasAccess() => false; + + void ExitBackingAliasAccess() + { + } + + bool TryResolveBackingAlias(ulong address, ulong size, out ulong alias) + { + alias = 0; + return false; + } } diff --git a/tests/SharpEmu.Libs.Tests/Memory/GuestMemory/SharedBackingTransferTests.cs b/tests/SharpEmu.Libs.Tests/Memory/GuestMemory/SharedBackingTransferTests.cs index ce52d5f..b04a822 100644 --- a/tests/SharpEmu.Libs.Tests/Memory/GuestMemory/SharedBackingTransferTests.cs +++ b/tests/SharpEmu.Libs.Tests/Memory/GuestMemory/SharedBackingTransferTests.cs @@ -114,6 +114,31 @@ public sealed unsafe class SharedBackingTransferTests Assert.Equal(Marker, *(ulong*)(mapping.Address + Segment)); } + [Fact] + public void AResolvedAliasReadsTheViewBytesUntilTheMappingChanges() + { + if (!Supported) return; + using var mapping = new TransferMappings(); + Assert.True(mapping.Store.TryWriteBacking(mapping.Address + Segment + 8, BitConverter.GetBytes(Marker))); + var snapshot = mapping.Store.AliasSnapshot; + Assert.True(mapping.Store.TryEnterAliasAccess()); + try + { + Assert.True(mapping.Store.TryResolveAlias(mapping.Address + Segment, 16, out var alias)); + Assert.Equal(Marker, *(ulong*)(alias + 8)); + Assert.Same(snapshot, mapping.Store.AliasSnapshot); + Assert.False(mapping.Store.TryResolveAlias(mapping.Address + Segment - 8, 16, out _)); + } + finally + { + mapping.Store.ExitAliasAccess(); + } + + Assert.True(mapping.Store.Unmap(mapping.Address + Segment, Segment, out _)); + Assert.NotSame(snapshot, mapping.Store.AliasSnapshot); + Assert.False(mapping.Store.TryResolveAlias(mapping.Address + Segment, 16, out _)); + } + private sealed class TransferMappings : IDisposable { public IHostViewMemory Host { get; } = HostViewMemory.Create();