RegionHandle.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. using Ryujinx.Memory.Range;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. namespace Ryujinx.Memory.Tracking
  6. {
  7. /// <summary>
  8. /// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
  9. /// and an action can be performed when the region is read to or written from.
  10. /// </summary>
  11. public class RegionHandle : IRegionHandle, IRange
  12. {
  13. public bool Dirty { get; private set; }
  14. public ulong Address { get; }
  15. public ulong Size { get; }
  16. public ulong EndAddress { get; }
  17. internal IMultiRegionHandle Parent { get; set; }
  18. internal int SequenceNumber { get; set; }
  19. private event Action _onDirty;
  20. private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access.
  21. private readonly List<VirtualRegion> _regions;
  22. private readonly MemoryTracking _tracking;
  23. private bool _disposed;
  24. internal MemoryPermission RequiredPermission => _preAction != null ? MemoryPermission.None : (Dirty ? MemoryPermission.ReadAndWrite : MemoryPermission.Read);
  25. internal RegionSignal PreAction => _preAction;
  26. /// <summary>
  27. /// Create a new region handle. The handle is registered with the given tracking object,
  28. /// and will be notified of any changes to the specified region.
  29. /// </summary>
  30. /// <param name="tracking">Tracking object for the target memory block</param>
  31. /// <param name="address">Virtual address of the region to track</param>
  32. /// <param name="size">Size of the region to track</param>
  33. /// <param name="dirty">Initial value of the dirty flag</param>
  34. internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool dirty = true)
  35. {
  36. Dirty = dirty;
  37. Address = address;
  38. Size = size;
  39. EndAddress = address + size;
  40. _tracking = tracking;
  41. _regions = tracking.GetVirtualRegionsForHandle(address, size);
  42. foreach (var region in _regions)
  43. {
  44. region.Handles.Add(this);
  45. }
  46. }
  47. /// <summary>
  48. /// Signal that a memory action occurred within this handle's virtual regions.
  49. /// </summary>
  50. /// <param name="write">Whether the region was written to or read</param>
  51. internal void Signal(ulong address, ulong size, bool write)
  52. {
  53. RegionSignal action = Interlocked.Exchange(ref _preAction, null);
  54. action?.Invoke(address, size);
  55. if (write)
  56. {
  57. bool oldDirty = Dirty;
  58. Dirty = true;
  59. if (!oldDirty)
  60. {
  61. _onDirty?.Invoke();
  62. }
  63. Parent?.SignalWrite();
  64. }
  65. }
  66. /// <summary>
  67. /// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
  68. /// </summary>
  69. public void Reprotect(bool asDirty = false)
  70. {
  71. Dirty = asDirty;
  72. lock (_tracking.TrackingLock)
  73. {
  74. foreach (VirtualRegion region in _regions)
  75. {
  76. region.UpdateProtection();
  77. }
  78. }
  79. }
  80. /// <summary>
  81. /// Register an action to perform when the tracked region is read or written.
  82. /// The action is automatically removed after it runs.
  83. /// </summary>
  84. /// <param name="action">Action to call on read or write</param>
  85. public void RegisterAction(RegionSignal action)
  86. {
  87. RegionSignal lastAction = Interlocked.Exchange(ref _preAction, action);
  88. if (lastAction == null && action != lastAction)
  89. {
  90. lock (_tracking.TrackingLock)
  91. {
  92. foreach (VirtualRegion region in _regions)
  93. {
  94. region.UpdateProtection();
  95. }
  96. }
  97. }
  98. }
  99. /// <summary>
  100. /// Register an action to perform when the region is written to.
  101. /// This action will not be removed when it is called - it is called each time the dirty flag is set.
  102. /// </summary>
  103. /// <param name="action">Action to call on dirty</param>
  104. public void RegisterDirtyEvent(Action action)
  105. {
  106. _onDirty += action;
  107. }
  108. /// <summary>
  109. /// Add a child virtual region to this handle.
  110. /// </summary>
  111. /// <param name="region">Virtual region to add as a child</param>
  112. internal void AddChild(VirtualRegion region)
  113. {
  114. _regions.Add(region);
  115. }
  116. /// <summary>
  117. /// Check if this region overlaps with another.
  118. /// </summary>
  119. /// <param name="address">Base address</param>
  120. /// <param name="size">Size of the region</param>
  121. /// <returns>True if overlapping, false otherwise</returns>
  122. public bool OverlapsWith(ulong address, ulong size)
  123. {
  124. return Address < address + size && address < EndAddress;
  125. }
  126. /// <summary>
  127. /// Dispose the handle. Within the tracking lock, this removes references from virtual and physical regions.
  128. /// </summary>
  129. public void Dispose()
  130. {
  131. if (_disposed)
  132. {
  133. throw new ObjectDisposedException(GetType().FullName);
  134. }
  135. _disposed = true;
  136. lock (_tracking.TrackingLock)
  137. {
  138. foreach (VirtualRegion region in _regions)
  139. {
  140. region.RemoveHandle(this);
  141. }
  142. }
  143. }
  144. }
  145. }