/// <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> /// Signal that a memory action occurred within this handle's virtual regions. /// </summary> /// <param name="address">Address accessed</param> /// <param name="size">Size of the region affected in bytes</param> /// <param name="write">Whether the region was written to or read</param> /// <param name="handleIterable">Reference to the handles being iterated, in case the list needs to be copied</param> internal void Signal(ulong address, ulong size, bool write, ref IList <RegionHandle> handleIterable) { // If this handle was already unmapped (even if just partially), // then we have nothing to do until it is mapped again. // The pre-action should be still consumed to avoid flushing on remap. if (Unmapped) { Interlocked.Exchange(ref _preAction, null); return; } if (_preAction != null) { // Copy the handles list in case it changes when we're out of the lock. if (handleIterable is List <RegionHandle> ) { handleIterable = handleIterable.ToArray(); } // Temporarily release the tracking lock while we're running the action. Monitor.Exit(_tracking.TrackingLock); try { lock (_preActionLock) { _preAction?.Invoke(address, size); // The action is removed after it returns, to ensure that the null check above succeeds when // it's still in progress rather than continuing and possibly missing a required data flush. Interlocked.Exchange(ref _preAction, null); } } finally { Monitor.Enter(_tracking.TrackingLock); } } if (write) { bool oldDirty = Dirty; Dirty = true; if (!oldDirty) { _onDirty?.Invoke(); } Parent?.SignalWrite(); } }