aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory/Tracking/RegionHandle.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Memory/Tracking/RegionHandle.cs')
-rw-r--r--Ryujinx.Memory/Tracking/RegionHandle.cs134
1 files changed, 134 insertions, 0 deletions
diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs
new file mode 100644
index 00000000..c00d039b
--- /dev/null
+++ b/Ryujinx.Memory/Tracking/RegionHandle.cs
@@ -0,0 +1,134 @@
+using Ryujinx.Memory.Range;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.Memory.Tracking
+{
+ /// <summary>
+ /// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
+ /// and an action can be performed when the region is read to or written from.
+ /// </summary>
+ public class RegionHandle : IRegionHandle, IRange
+ {
+ public bool Dirty { get; private set; } = true;
+
+ public ulong Address { get; }
+ public ulong Size { get; }
+ public ulong EndAddress { get; }
+
+ internal IMultiRegionHandle Parent { get; set; }
+ internal int SequenceNumber { get; set; }
+
+ private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access.
+ private readonly List<VirtualRegion> _regions;
+ private readonly MemoryTracking _tracking;
+
+ internal MemoryPermission RequiredPermission => _preAction != null ? MemoryPermission.None : (Dirty ? MemoryPermission.ReadAndWrite : MemoryPermission.Read);
+
+ /// <summary>
+ /// Create a new region handle. The handle is registered with the given tracking object,
+ /// and will be notified of any changes to the specified region.
+ /// </summary>
+ /// <param name="tracking">Tracking object for the target memory block</param>
+ /// <param name="address">Virtual address of the region to track</param>
+ /// <param name="size">Size of the region to track</param>
+ internal RegionHandle(MemoryTracking tracking, ulong address, ulong size)
+ {
+ Address = address;
+ Size = size;
+ EndAddress = address + size;
+
+ _tracking = tracking;
+ _regions = tracking.GetVirtualRegionsForHandle(address, size);
+ foreach (var region in _regions)
+ {
+ region.Handles.Add(this);
+ }
+ }
+
+ /// <summary>
+ /// Signal that a memory action occurred within this handle's virtual regions.
+ /// </summary>
+ /// <param name="write">Whether the region was written to or read</param>
+ internal void Signal(ulong address, ulong size, bool write)
+ {
+ RegionSignal action = Interlocked.Exchange(ref _preAction, null);
+ action?.Invoke(address, size);
+
+ if (write)
+ {
+ Dirty = true;
+ Parent?.SignalWrite();
+ }
+ }
+
+ /// <summary>
+ /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
+ /// </summary>
+ public void Reprotect()
+ {
+ Dirty = false;
+ lock (_tracking.TrackingLock)
+ {
+ foreach (VirtualRegion region in _regions)
+ {
+ region.UpdateProtection();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Register an action to perform when the tracked region is read or written.
+ /// The action is automatically removed after it runs.
+ /// </summary>
+ /// <param name="action">Action to call on read or write</param>
+ public void RegisterAction(RegionSignal action)
+ {
+ RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action);
+ if (lastAction == null && action != lastAction)
+ {
+ lock (_tracking.TrackingLock)
+ {
+ foreach (VirtualRegion region in _regions)
+ {
+ region.UpdateProtection();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Add a child virtual region to this handle.
+ /// </summary>
+ /// <param name="region">Virtual region to add as a child</param>
+ internal void AddChild(VirtualRegion region)
+ {
+ _regions.Add(region);
+ }
+
+ /// <summary>
+ /// Check if this region overlaps with another.
+ /// </summary>
+ /// <param name="address">Base address</param>
+ /// <param name="size">Size of the region</param>
+ /// <returns>True if overlapping, false otherwise</returns>
+ public bool OverlapsWith(ulong address, ulong size)
+ {
+ return Address < address + size && address < EndAddress;
+ }
+
+ /// <summary>
+ /// Dispose the handle. Within the tracking lock, this removes references from virtual and physical regions.
+ /// </summary>
+ public void Dispose()
+ {
+ lock (_tracking.TrackingLock)
+ {
+ foreach (VirtualRegion region in _regions)
+ {
+ region.RemoveHandle(this);
+ }
+ }
+ }
+ }
+}