public void SmartRegionTracking() { // Smart multi region handles dynamically change their tracking granularity based on QueryMemory calls. // This can save on reprotects on larger resources. const int pageCount = 32; IMultiRegionHandle handle = GetGranular(true, 0, PageSize * pageCount, PageSize); // Query some large regions to prep the subdivision of the tracking region. int[] regionSizes = new int[] { 6, 4, 3, 2, 6, 1 }; ulong address = 0; for (int i = 0; i < regionSizes.Length; i++) { int region = regionSizes[i]; handle.QueryModified(address, (ulong)(PageSize * region), (address, size) => { }); // There should be a gap between regions, // So that they don't combine and we can see the full effects. address += (ulong)(PageSize * (region + 1)); } // Clear modified. handle.QueryModified((address, size) => { }); // Trigger each region with a 1 byte write. address = 0; for (int i = 0; i < regionSizes.Length; i++) { int region = regionSizes[i]; _tracking.VirtualMemoryEvent(address, 1, true); address += (ulong)(PageSize * (region + 1)); } int regionInd = 0; ulong expectedAddress = 0; // Expect each region to trigger in its entirety, in address ascending order. handle.QueryModified((address, size) => { int region = regionSizes[regionInd++]; Assert.AreEqual(address, expectedAddress); Assert.AreEqual(size, (ulong)(PageSize * region)); expectedAddress += (ulong)(PageSize * (region + 1)); }); }
private void PreparePages(IMultiRegionHandle handle, int pageCount, ulong address = 0) { Random random = new Random(); // Make sure the list has minimum granularity (smart region changes granularity based on requested ranges) RandomOrder(random, Enumerable.Range(0, pageCount).ToList(), (i) => { ulong resultAddress = ulong.MaxValue; handle.QueryModified((ulong)i * PageSize + address, PageSize, (address, range) => { resultAddress = address; }); Assert.AreEqual(resultAddress, (ulong)i * PageSize + address); }); }
private int ExpectQueryInOrder(IMultiRegionHandle handle, ulong startAddress, ulong size, Func <ulong, bool> addressPredicate, int sequenceNumber) { int regionCount = 0; ulong lastAddress = startAddress; handle.QueryModified(startAddress, size, (address, range) => { Assert.IsTrue(addressPredicate(address)); // Written pages must be even. Assert.GreaterOrEqual(address, lastAddress); // Must be signalled in ascending order, regardless of write order. lastAddress = address; regionCount++; }, sequenceNumber); return(regionCount); }
public void SequenceNumber([Values] bool smart) { // The sequence number can be used to ignore dirty flags, and defer their consumption until later. // If a user consumes a dirty flag with sequence number 1, then there is a write to the protected region, // the dirty flag will not be acknowledged until the sequence number is 2. // This is useful for situations where we know that the data was complete when the sequence number was set. // ...essentially, when that data can only be updated on a future sequence number. const int pageCount = 32; IMultiRegionHandle handle = GetGranular(smart, 0, PageSize * pageCount, PageSize); PreparePages(handle, pageCount); Random random = new Random(); IEnumerable <int> halfRange = Enumerable.Range(0, pageCount / 2); List <int> odd = halfRange.Select(x => x * 2 + 1).ToList(); List <int> even = halfRange.Select(x => x * 2).ToList(); // Write to all the odd pages. RandomOrder(random, odd, (i) => { _tracking.VirtualMemoryEvent((ulong)i * PageSize, PageSize, true); }); int oddRegionCount = 0; // Track with sequence number 1. Future dirty flags should only be consumed with sequence number != 1. // Only track the odd pages, so the even ones don't have their sequence number set. foreach (int index in odd) { handle.QueryModified((ulong)index * PageSize, PageSize, (address, range) => { oddRegionCount++; }, 1); } Assert.AreEqual(oddRegionCount, pageCount / 2); // Must have written to all odd pages. // Write to all pages. _tracking.VirtualMemoryEvent(0, PageSize * pageCount, true); // Only the even regions should be reported for sequence number 1. int evenRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 0, 1); Assert.AreEqual(evenRegionCount, pageCount / 2); // Must have written to all even pages. oddRegionCount = 0; handle.QueryModified(0, PageSize * pageCount, (address, range) => { oddRegionCount++; }, 1); Assert.AreEqual(oddRegionCount, 0); // Sequence number has not changed, so found no dirty subregions. // With sequence number 2, all all pages should be reported as modified. oddRegionCount = ExpectQueryInOrder(handle, 0, PageSize * pageCount, (address) => (address / PageSize) % 2 == 1, 2); Assert.AreEqual(oddRegionCount, pageCount / 2); // Must have written to all odd pages. }