public void DisposeHandles() { // Ensure that disposed handles correctly remove their virtual and physical regions. RegionHandle handle = _tracking.BeginTracking(0, PageSize); handle.Reprotect(); Assert.AreEqual((1, 1), _tracking.GetRegionCounts()); handle.Dispose(); Assert.AreEqual((0, 0), _tracking.GetRegionCounts()); // Two handles, small entirely contains big. // We expect there to be three regions after creating both, one for the small region and two covering the big one around it. // Regions are always split to avoid overlapping, which is why there are three instead of two. RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize); RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4); Assert.AreEqual((3, 3), _tracking.GetRegionCounts()); // After disposing the big region, only the small one will remain. handleBig.Dispose(); Assert.AreEqual((1, 1), _tracking.GetRegionCounts()); handleSmall.Dispose(); Assert.AreEqual((0, 0), _tracking.GetRegionCounts()); }
public void SingleRegion() { RegionHandle handle = _tracking.BeginTracking(0, PageSize); (ulong address, ulong size)? readTrackingTriggered = null; handle.RegisterAction((address, size) => { readTrackingTriggered = (address, size); }); bool dirtyInitial = handle.Dirty; Assert.True(dirtyInitial); // Handle starts dirty. handle.Reprotect(); bool dirtyAfterReprotect = handle.Dirty; Assert.False(dirtyAfterReprotect); // Handle is no longer dirty. _tracking.VirtualMemoryEvent(PageSize * 2, 4, true); _tracking.VirtualMemoryEvent(PageSize * 2, 4, false); bool dirtyAfterUnrelatedReadWrite = handle.Dirty; Assert.False(dirtyAfterUnrelatedReadWrite); // Not dirtied, as the write was to an unrelated address. Assert.IsNull(readTrackingTriggered); // Hasn't been triggered yet _tracking.VirtualMemoryEvent(0, 4, false); bool dirtyAfterRelatedRead = handle.Dirty; Assert.False(dirtyAfterRelatedRead); // Only triggers on write. Assert.AreEqual(readTrackingTriggered, (0UL, 4UL)); // Read action was triggered. readTrackingTriggered = null; _tracking.VirtualMemoryEvent(0, 4, true); bool dirtyAfterRelatedWrite = handle.Dirty; Assert.True(dirtyAfterRelatedWrite); // Dirty flag should now be set. _tracking.VirtualMemoryEvent(4, 4, true); bool dirtyAfterRelatedWrite2 = handle.Dirty; Assert.True(dirtyAfterRelatedWrite2); // Dirty flag should still be set. handle.Reprotect(); bool dirtyAfterReprotect2 = handle.Dirty; Assert.False(dirtyAfterReprotect2); // Handle is no longer dirty. handle.Dispose(); bool dirtyAfterDispose = TestSingleWrite(handle, 0, 4); Assert.False(dirtyAfterDispose); // Handle cannot be triggered when disposed }
protected override LResult WindowProcedure(WindowHandle window, MessageType message, WParam wParam, LParam lParam) { switch (message) { case MessageType.Size: cxClient = lParam.LowWord; cyClient = lParam.HighWord; CursorHandle hCursor = Windows.SetCursor(CursorId.Wait); Windows.ShowCursor(true); hRgnClip.Dispose(); Span <RegionHandle> hRgnTemp = stackalloc RegionHandle[6]; hRgnTemp[0] = Gdi.CreateEllipticRegion(Rectangle.FromLTRB(0, cyClient / 3, cxClient / 2, 2 * cyClient / 3)); hRgnTemp[1] = Gdi.CreateEllipticRegion(Rectangle.FromLTRB(cxClient / 2, cyClient / 3, cxClient, 2 * cyClient / 3)); hRgnTemp[2] = Gdi.CreateEllipticRegion(Rectangle.FromLTRB(cxClient / 3, 0, 2 * cxClient / 3, cyClient / 2)); hRgnTemp[3] = Gdi.CreateEllipticRegion(Rectangle.FromLTRB(cxClient / 3, cyClient / 2, 2 * cxClient / 3, cyClient)); hRgnTemp[4] = Gdi.CreateRectangleRegion(Rectangle.FromLTRB(0, 0, 1, 1)); hRgnTemp[5] = Gdi.CreateRectangleRegion(Rectangle.FromLTRB(0, 0, 1, 1)); hRgnClip = Gdi.CreateRectangleRegion(Rectangle.FromLTRB(0, 0, 1, 1)); hRgnTemp[4].CombineRegion(hRgnTemp[0], hRgnTemp[1], CombineRegionMode.Or); hRgnTemp[5].CombineRegion(hRgnTemp[2], hRgnTemp[3], CombineRegionMode.Or); hRgnClip.CombineRegion(hRgnTemp[4], hRgnTemp[5], CombineRegionMode.Xor); for (int i = 0; i < 6; i++) { hRgnTemp[i].Dispose(); } Windows.SetCursor(hCursor); Windows.ShowCursor(false); return(0); case MessageType.Paint: using (DeviceContext dc = window.BeginPaint()) { dc.SetViewportOrigin(new Point(cxClient / 2, cyClient / 2)); dc.SelectClippingRegion(hRgnClip); double fRadius = Hypotenuse(cxClient / 2.0, cyClient / 2.0); for (double fAngle = 0.0; fAngle < TWO_PI; fAngle += TWO_PI / 360) { dc.MoveTo(default); dc.LineTo(new Point( (int)(fRadius * Math.Cos(fAngle) + 0.5), (int)(-fRadius * Math.Sin(fAngle) + 0.5))); } }
public void Dispose() => _impl.Dispose();
public void Multithreading() { // Multithreading sanity test // Multiple threads can easily read/write memory regions from any existing handle. // Handles can also be owned by different threads, though they should have one owner thread. // Handles can be created and disposed at any time, by any thread. // This test should not throw or deadlock due to invalid state. const int threadCount = 1; const int handlesPerThread = 16; long finishedTime = 0; RegionHandle[] handles = new RegionHandle[threadCount * handlesPerThread]; Random globalRand = new Random(); for (int i = 0; i < handles.Length; i++) { handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); handles[i].Reprotect(); } List <Thread> testThreads = new List <Thread>(); // Dirty flag consumer threads int dirtyFlagReprotects = 0; for (int i = 0; i < threadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { int handleBase = randSeed * handlesPerThread; while (Stopwatch.GetTimestamp() < finishedTime) { Random random = new Random(randSeed); RegionHandle handle = handles[handleBase + random.Next(handlesPerThread)]; if (handle.Dirty) { handle.Reprotect(); Interlocked.Increment(ref dirtyFlagReprotects); } } })); } // Write trigger threads int writeTriggers = 0; for (int i = 0; i < threadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { Random random = new Random(randSeed); ulong handleBase = (ulong)(randSeed * handlesPerThread * PageSize); while (Stopwatch.GetTimestamp() < finishedTime) { _tracking.VirtualMemoryEvent(handleBase + (ulong)random.Next(PageSize * handlesPerThread), PageSize / 2, true); Interlocked.Increment(ref writeTriggers); } })); } // Handle create/delete threads int handleLifecycles = 0; for (int i = 0; i < threadCount; i++) { int randSeed = i; testThreads.Add(new Thread(() => { int maxAddress = threadCount * handlesPerThread * PageSize; Random random = new Random(randSeed + 512); while (Stopwatch.GetTimestamp() < finishedTime) { RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536)); handle.Dispose(); Interlocked.Increment(ref handleLifecycles); } })); } finishedTime = Stopwatch.GetTimestamp() + Stopwatch.Frequency / 2; // Run for 500ms; foreach (Thread thread in testThreads) { thread.Start(); } foreach (Thread thread in testThreads) { thread.Join(); } Assert.Greater(dirtyFlagReprotects, 10); Assert.Greater(writeTriggers, 10); Assert.Greater(handleLifecycles, 10); }
public void ReadAndWriteProtection() { MemoryPermission protection = MemoryPermission.ReadAndWrite; _memoryManager.OnProtect += (va, size, newProtection) => { Assert.AreEqual((0, PageSize), (va, size)); // Should protect the exact region all the operations use. protection = newProtection; }; RegionHandle handle = _tracking.BeginTracking(0, PageSize); // After creating the handle, there is no protection yet. Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); bool dirtyInitial = handle.Dirty; Assert.True(dirtyInitial); // Handle starts dirty. handle.Reprotect(); // After a reprotect, there is write protection, which will set a dirty flag when any write happens. Assert.AreEqual(MemoryPermission.Read, protection); (ulong address, ulong size)? readTrackingTriggered = null; handle.RegisterAction((address, size) => { readTrackingTriggered = (address, size); }); // Registering an action adds read/write protection. Assert.AreEqual(MemoryPermission.None, protection); bool dirtyAfterReprotect = handle.Dirty; Assert.False(dirtyAfterReprotect); // Handle is no longer dirty. // First we should read, which will trigger the action. This _should not_ remove write protection on the memory. _tracking.VirtualMemoryEvent(0, 4, false); bool dirtyAfterRead = handle.Dirty; Assert.False(dirtyAfterRead); // Not dirtied, as this was a read. Assert.AreEqual(readTrackingTriggered, (0UL, 4UL)); // Read action was triggered. Assert.AreEqual(MemoryPermission.Read, protection); // Write protection is still present. readTrackingTriggered = null; // Now, perform a write. _tracking.VirtualMemoryEvent(0, 4, true); bool dirtyAfterWriteAfterRead = handle.Dirty; Assert.True(dirtyAfterWriteAfterRead); // Should be dirty. Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); // All protection is now be removed from the memory. Assert.IsNull(readTrackingTriggered); // Read tracking was removed when the action fired, as it can only fire once. handle.Dispose(); }
static LRESULT WindowProcedure(WindowHandle window, WindowMessage message, WPARAM wParam, LPARAM lParam) { switch (message) { case WindowMessage.Size: cxClient = lParam.LowWord; cyClient = lParam.HighWord; CursorHandle hCursor = Windows.SetCursor(CursorId.Wait); Windows.ShowCursor(true); hRgnClip?.Dispose(); RegionHandle[] hRgnTemp = new RegionHandle[6]; hRgnTemp[0] = Windows.CreateEllipticRegion(0, cyClient / 3, cxClient / 2, 2 * cyClient / 3); hRgnTemp[1] = Windows.CreateEllipticRegion(cxClient / 2, cyClient / 3, cxClient, 2 * cyClient / 3); hRgnTemp[2] = Windows.CreateEllipticRegion(cxClient / 3, 0, 2 * cxClient / 3, cyClient / 2); hRgnTemp[3] = Windows.CreateEllipticRegion(cxClient / 3, cyClient / 2, 2 * cxClient / 3, cyClient); hRgnTemp[4] = Windows.CreateRectangleRegion(0, 0, 1, 1); hRgnTemp[5] = Windows.CreateRectangleRegion(0, 0, 1, 1); hRgnClip = Windows.CreateRectangleRegion(0, 0, 1, 1); hRgnTemp[4].CombineRegion(hRgnTemp[0], hRgnTemp[1], CombineRegionMode.Or); hRgnTemp[5].CombineRegion(hRgnTemp[2], hRgnTemp[3], CombineRegionMode.Or); hRgnClip.CombineRegion(hRgnTemp[4], hRgnTemp[5], CombineRegionMode.Xor); for (int i = 0; i < 6; i++) { hRgnTemp[i]?.Dispose(); } Windows.SetCursor(hCursor); Windows.ShowCursor(false); return(0); case WindowMessage.Paint: using (DeviceContext dc = window.BeginPaint()) { dc.SetViewportOrigin(cxClient / 2, cyClient / 2); dc.SelectClippingRegion(hRgnClip); double fRadius = Hypotenuse(cxClient / 2.0, cyClient / 2.0); for (double fAngle = 0.0; fAngle < TWO_PI; fAngle += TWO_PI / 360) { dc.MoveTo(0, 0); dc.LineTo( (int)(fRadius * Math.Cos(fAngle) + 0.5), (int)(-fRadius * Math.Sin(fAngle) + 0.5)); } } return(0); case WindowMessage.Destroy: hRgnClip?.Dispose(); Windows.PostQuitMessage(0); return(0); } return(Windows.DefaultWindowProcedure(window, message, wParam, lParam)); }