[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.
This commit is contained in:
ParantezTech 2026-10-02 18:48:24 +03:00
parent e47798a854
commit 8324faed84
5 changed files with 93 additions and 2 deletions

View File

@ -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.

View File

@ -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<byte> 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);

View File

@ -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;
}

View File

@ -24,4 +24,18 @@ public interface IGuestBackedSpace
// Copies through the backing alias only: no protection change, no store notification.
bool TryWriteBacking(ulong address, ReadOnlySpan<byte> data);
bool TryReadBacking(ulong address, Span<byte> data);
object? BackingAliasSnapshot => null;
bool TryEnterBackingAliasAccess() => false;
void ExitBackingAliasAccess()
{
}
bool TryResolveBackingAlias(ulong address, ulong size, out ulong alias)
{
alias = 0;
return false;
}
}

View File

@ -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();