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 }
public void OverlappingRegions() { RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16); allHandle.Reprotect(); (ulong address, ulong size)? readTrackingTriggeredAll = null; Action registerReadAction = () => { readTrackingTriggeredAll = null; allHandle.RegisterAction((address, size) => { readTrackingTriggeredAll = (address, size); }); }; registerReadAction(); // Create 16 page sized handles contained within the allHandle. RegionHandle[] containedHandles = new RegionHandle[16]; for (int i = 0; i < 16; i++) { containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); containedHandles[i].Reprotect(); } for (int i = 0; i < 16; i++) { // No handles are dirty. Assert.False(allHandle.Dirty); Assert.IsNull(readTrackingTriggeredAll); for (int j = 0; j < 16; j++) { Assert.False(containedHandles[j].Dirty); } _tracking.VirtualMemoryEvent((ulong)i * PageSize, 1, true); // Only the handle covering the entire range and the relevant contained handle are dirty. Assert.True(allHandle.Dirty); Assert.AreEqual(readTrackingTriggeredAll, ((ulong)i * PageSize, 1UL)); // Triggered read tracking for (int j = 0; j < 16; j++) { if (j == i) { Assert.True(containedHandles[j].Dirty); } else { Assert.False(containedHandles[j].Dirty); } } // Clear flags and reset read action. registerReadAction(); allHandle.Reprotect(); containedHandles[i].Reprotect(); } }
public void PhysicalMemoryMapping() { // Tracking is done in the virtual space usually, but we also support tracking on physical regions. // The physical regions that make up a virtual region are determined when the region is created, // or when a mapping changes. // These tests verify that the region cannot be signalled after unmapping, and can after remapping. RegionHandle handle = _tracking.BeginTracking(PageSize, PageSize); Assert.True(handle.Dirty); bool trackedWriteTriggers = TestSingleWrite(handle, PageSize, 1, true); Assert.True(trackedWriteTriggers); _memoryManager.NoMappings = true; _tracking.Unmap(PageSize, PageSize); bool unmappedWriteTriggers = TestSingleWrite(handle, PageSize, 1, true); Assert.False(unmappedWriteTriggers); _memoryManager.NoMappings = false; _tracking.Map(PageSize, PageSize, PageSize); bool remappedWriteTriggers = TestSingleWrite(handle, PageSize, 1, true); Assert.True(remappedWriteTriggers); }
private ConcurrentDictionary <NodeHandle, NodeEdges> regionFactory(RegionHandle region) { var ret = new ConcurrentDictionary <NodeHandle, NodeEdges>(); ReadFromFile(region, ret); recentRegions.AddOrUpdate(region, TotalTime.ElapsedMilliseconds); return(ret); }
public NodeEdges AddOrUpdate(NodeHandle n, NodeEdges newValue, Func <NodeHandle, NodeEdges, NodeEdges> valueFactory) { RegionHandle r = Region(n); recentRegions.AddOrUpdate(r, TotalTime.ElapsedMilliseconds); SetDirty(r); return(Regions.GetOrAdd(r, regionFactory).AddOrUpdate(n, newValue, valueFactory)); }
private bool TestSingleWrite(RegionHandle handle, ulong address, ulong size) { handle.Reprotect(); _tracking.VirtualMemoryEvent(address, size, true); return(handle.Dirty); }
protected override void DeSerialise(byte[] buf, ref int o, int length) { SoundId = BinarySerializer.DeSerializeGuid(buf, ref o, length); OwnerId = BinarySerializer.DeSerializeGuid(buf, ref o, length); ObjectId = BinarySerializer.DeSerializeGuid(buf, ref o, length); ParentId = BinarySerializer.DeSerializeGuid(buf, ref o, length); Handle = new RegionHandle(BinarySerializer.DeSerializeUInt64_Le(buf, ref o, length)); Position = BinarySerializer.DeSerializeVector3(buf, ref o, buf.Length); }
public void PreciseAction() { RegionHandle handle = _tracking.BeginTracking(0, PageSize); (ulong address, ulong size, bool write)? preciseTriggered = null; handle.RegisterPreciseAction((address, size, write) => { preciseTriggered = (address, size, write); return(true); }); (ulong address, ulong size)? readTrackingTriggered = null; handle.RegisterAction((address, size) => { readTrackingTriggered = (address, size); }); handle.Reprotect(); _tracking.VirtualMemoryEvent(0, 4, false, precise: true); Assert.IsNull(readTrackingTriggered); // Hasn't been triggered - precise action returned true. Assert.AreEqual(preciseTriggered, (0UL, 4UL, false)); // Precise action was triggered. _tracking.VirtualMemoryEvent(0, 4, true, precise: true); Assert.IsNull(readTrackingTriggered); // Still hasn't been triggered. bool dirtyAfterPreciseActionTrue = handle.Dirty; Assert.False(dirtyAfterPreciseActionTrue); // Not dirtied - precise action returned true. Assert.AreEqual(preciseTriggered, (0UL, 4UL, true)); // Precise action was triggered. // Handle is now dirty. handle.Reprotect(true); preciseTriggered = null; _tracking.VirtualMemoryEvent(4, 4, true, precise: true); Assert.AreEqual(preciseTriggered, (4UL, 4UL, true)); // Precise action was triggered even though handle was dirty. handle.Reprotect(); handle.RegisterPreciseAction((address, size, write) => { preciseTriggered = (address, size, write); return(false); // Now, we return false, which indicates that the regular read/write behaviours should trigger. }); _tracking.VirtualMemoryEvent(8, 4, true, precise: true); Assert.AreEqual(readTrackingTriggered, (8UL, 4UL)); // Read action triggered, as precise action returned false. bool dirtyAfterPreciseActionFalse = handle.Dirty; Assert.True(dirtyAfterPreciseActionFalse); // Dirtied, as precise action returned false. Assert.AreEqual(preciseTriggered, (8UL, 4UL, true)); // Precise action was triggered. }
public static void ReadFromFile(RegionHandle region, ConcurrentDictionary <NodeHandle, NodeEdges> output) { try { totalReadTimer.Start(); uint r = (uint)region >> 7; ReadFromFile(region, $"scripts/NavMesh/{r}/{region}.mesh", output); } finally { totalReadTimer.Stop(); } }
protected override void DeSerialise(byte[] buf, ref int o, int length) { AgentId = BinarySerializer.DeSerializeGuid(buf, ref o, length); SessionId = BinarySerializer.DeSerializeGuid(buf, ref o, length); Position = BinarySerializer.DeSerializeVector3(buf, ref o, length); LookAt = BinarySerializer.DeSerializeVector3(buf, ref o, length); RegionHandle = new RegionHandle(BinarySerializer.DeSerializeUInt64_Le(buf, ref o, length)); TimeStamp = BinarySerializer.DeSerializeDateTime(buf, ref o, length); ChannelVersion = BinarySerializer.DeSerializeString(buf, ref o, length, 2); }
public void Set(NodeHandle n, NodeEdges e) { RegionHandle r = Region(n); Regions .GetOrAdd(r, regionFactory) .AddOrUpdate(n, e, (k, o) => e); recentRegions.AddOrUpdate(r, TotalTime.ElapsedMilliseconds); SetDirty(r); }
public NodeEdges Get(NodeHandle n) { RegionHandle r = Region(n); if (Regions.GetOrAdd(r, regionFactory).TryGetValue(n, out NodeEdges edges)) { recentRegions.AddOrUpdate(r, TotalTime.ElapsedMilliseconds); return(edges); } return(NodeEdges.Empty); }
private bool TestSingleWrite(RegionHandle handle, ulong address, ulong size, bool physical = false) { handle.Reprotect(); if (physical) { _tracking.PhysicalMemoryEvent(address, true); } else { _tracking.VirtualMemoryEvent(address, size, true); } return(handle.Dirty); }
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 static void ReadFromFile(RegionHandle region, string filename, ConcurrentDictionary <NodeHandle, NodeEdges> output) { if (!LoadEnabled) { return; } var s = new Stopwatch(); s.Start(); try { using (BinaryReader r = Codec.Reader(filename)) { readBytesTimer.Start(); int magic = r.ReadInt32(); if (magic == 0x000FEED9) { int count = r.ReadInt32(); if (count <= 0) { Log($"Invalid count: {count}"); return; } byte[] buf; ulong[] handles = new ulong[count]; ulong[] edges = new ulong[count]; buf = r.ReadBytes(count * sizeof(ulong)); Buffer.BlockCopy(buf, 0, handles, 0, buf.Length); buf = r.ReadBytes(count * sizeof(ulong)); Buffer.BlockCopy(buf, 0, edges, 0, buf.Length); readBytesTimer.Stop(); applyBytesTimer.Start(); Parallel.For(0, count, (i) => { output.TryAdd((NodeHandle)handles[i], (NodeEdges)edges[i]); }); Log($"[{region}] Loaded {count} (ver. 9) nodes in {s.ElapsedMilliseconds}ms"); applyBytesTimer.Stop(); } else { Log($"Invalid magic bytes: {magic}"); return; } } } catch (DirectoryNotFoundException) { // Log($"[{region}] Loaded 0 nodes ({filename} not found) in {s.ElapsedMilliseconds}ms"); } catch (FileNotFoundException) { // Log($"[{region}] Loaded 0 nodes ({filename} not found) in {s.ElapsedMilliseconds}ms"); } s.Stop(); return; }
public void PageAlignment( [Values(1ul, 512ul, 2048ul, 4096ul, 65536ul)][Random(1ul, 65536ul, RndCnt)] ulong address, [Values(1ul, 4ul, 1024ul, 4096ul, 65536ul)][Random(1ul, 65536ul, RndCnt)] ulong size) { ulong alignedStart = (address / PageSize) * PageSize; ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; ulong alignedSize = alignedEnd - alignedStart; RegionHandle handle = _tracking.BeginTracking(address, size); // Anywhere inside the pages the region is contained on should trigger. bool originalRangeTriggers = TestSingleWrite(handle, address, size); Assert.True(originalRangeTriggers); bool alignedRangeTriggers = TestSingleWrite(handle, alignedStart, alignedSize); Assert.True(alignedRangeTriggers); bool alignedStartTriggers = TestSingleWrite(handle, alignedStart, 1); Assert.True(alignedStartTriggers); bool alignedEndTriggers = TestSingleWrite(handle, alignedEnd - 1, 1); Assert.True(alignedEndTriggers); // Outside the tracked range should not trigger. bool alignedBeforeTriggers = TestSingleWrite(handle, alignedStart - 1, 1); Assert.False(alignedBeforeTriggers); bool alignedAfterTriggers = TestSingleWrite(handle, alignedEnd, 1); Assert.False(alignedAfterTriggers); }
internal static void SaveToFile(RegionHandle region) { Log($"Saving region {region}"); uint r = (uint)region >> 7; Directory.CreateDirectory($"scripts/NavMesh/{r}/"); var file = $"scripts/NavMesh/{r}/{region}.mesh"; using (BinaryWriter w = Codec.Writer(file + ".tmp")) { w.Write(versionBytes); var result = AllNodes.Regions[region]; // we know it's there bc it's dirty ulong[] handles = result.Keys.Cast <ulong>().ToArray(); ulong[] edges = handles.Select(h => (ulong)result[(NodeHandle)h]).ToArray(); // use Select this way to guarantee they match the order of handles byte[] buf; try { w.Write(handles.Length); buf = new byte[handles.Length * sizeof(NodeHandle)]; Buffer.BlockCopy(handles, 0, buf, 0, buf.Length); w.Write(buf); } catch (Exception err) { Log("Failed to write handles to file: " + err.ToString()); return; } try { buf = new byte[edges.Length * sizeof(ulong)]; Buffer.BlockCopy(edges, 0, buf, 0, buf.Length); w.Write(buf); } catch (Exception err) { Log("Failed to write edges to file: " + err.ToString()); return; } } try { File.Delete(file); } catch (FileNotFoundException) { } try { File.Move(file + ".tmp", file); } catch (Exception e) { Log("File.Move Failed: " + e.ToString()); } }
internal CpuRegionHandle(RegionHandle impl) { _impl = impl; }
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)); }
public Region AddRegion(RegionHandle handle, Host host) { Region region; Capability seedCapability = null; if (RegionByHandle.ContainsKey(handle)) { // Region already exists region = RegionByHandle[handle]; Host oldHost = region.Host; if (host == oldHost && region.Alive) { Logger.LogInfo($"Region with handle {handle} already exists and is alive, using existing region."); return(region); } if (host != oldHost) { Logger.LogWarning($"Region with handle {handle} already exists but with a different host. Removing and creating new."); } if (region.Alive == false) { Logger.LogWarning($"Region with handle {handle} already exists but it isn't alive. Removing and creating new."); } // Save capabilities seed URL seedCapability = region.GetCapability(Capability.SEED_CAPABILITY_NAME); // Kill the old host, and then we can continue on and add the new host. We have to kill even if the host // matches, because all the agent state for the new camera is completely different. RemoveRegion(oldHost); } else { Logger.LogInfo($"Region with handle {handle} does not exist, creating a new one."); } UInt32 iindex = handle.X; UInt32 jindex = handle.Y; int x = (int)(iindex / WIDTH); int y = (int)(jindex / WIDTH); Logger.LogInfo($"Adding new region {handle} on {host}."); Vector3Double origin_global = handle.ToVector3Double(); region = new Region(handle, host, WIDTH, WORLD_PATCH_SIZE, WIDTH_IN_METRES); if (seedCapability != null) { region.SetCapability(seedCapability); } RegionList.Add(region); ActiveRegionList.Add(region); CulledRegionList.Add(region); RegionByHandle[handle] = region; RegionByHost[host] = region; // Find all the adjacent regions, and attach them. // Generate handles for all of the adjacent regions, and attach them in the correct way. // connect the edges float adj_x = 0f; float adj_y = 0f; float region_x = handle.X; float region_y = handle.Y; RegionHandle adj_handle = new RegionHandle(0); float width = WIDTH_IN_METRES; // Iterate through all directions, and connect neighbors if there. for (int dir = 0; dir < 8; dir++) { adj_x = region_x + width * DirectionAxes[dir, 0]; adj_y = region_y + width * DirectionAxes[dir, 1]; if (adj_x >= 0) { adj_handle.X = (UInt32)adj_x; } if (adj_y >= 0) { adj_handle.Y = (UInt32)adj_y; } if (RegionByHandle.ContainsKey(adj_handle)) { region.ConnectNeighbour(RegionByHandle[adj_handle], (DirectionIndex)dir); } } // TODO: UpdateWaterObjects(); return(region); }
public static RegionType CombineRegion(this RegionHandle destination, RegionHandle sourceOne, RegionHandle sourceTwo, CombineRegionMode mode) => GdiMethods.CombineRegion(destination, sourceOne, sourceTwo, mode);
public static RegionType SelectClippingRegion(this DeviceContext deviceContext, RegionHandle region) => GdiMethods.SelectClippingRegion(deviceContext, region);
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 ReadActionThreadConsumption() { // Read actions should only be triggered once for each registration. // The implementation should use an interlocked exchange to make sure other threads can't get the action. RegionHandle handle = _tracking.BeginTracking(0, PageSize); int triggeredCount = 0; int registeredCount = 0; int signalThreadsDone = 0; bool isRegistered = false; Action registerReadAction = () => { registeredCount++; handle.RegisterAction((address, size) => { isRegistered = false; Interlocked.Increment(ref triggeredCount); }); }; const int threadCount = 16; const int iterationCount = 10000; Thread[] signalThreads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { int randSeed = i; signalThreads[i] = new Thread(() => { Random random = new Random(randSeed); for (int j = 0; j < iterationCount; j++) { _tracking.VirtualMemoryEvent((ulong)random.Next(PageSize), 4, false); } Interlocked.Increment(ref signalThreadsDone); }); } for (int i = 0; i < threadCount; i++) { signalThreads[i].Start(); } while (signalThreadsDone != -1) { if (signalThreadsDone == threadCount) { signalThreadsDone = -1; } if (!isRegistered) { isRegistered = true; registerReadAction(); } } // The action should trigger exactly once for every registration, // then we register once after all the threads signalling it cease. Assert.AreEqual(registeredCount, triggeredCount + 1); }
public void InheritHandles() { // Test merging the following into a granular region handle: // - 3x gap (creates new granular handles) // - 3x from multiregion: not dirty, dirty and with action // - 2x gap // - 3x single page: not dirty, dirty and with action // - 3x two page: not dirty, dirty and with action (handle is not reused, but its state is copied to the granular handles) // - 1x gap // For a total of 18 pages. bool[] actionsTriggered = new bool[3]; MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); PreparePages(granular, 3, PageSize * 3); // Write to the second handle in the multiregion. _tracking.VirtualMemoryEvent(PageSize * 4, PageSize, true); // Add an action to the third handle in the multiregion. granular.RegisterAction(PageSize * 5, PageSize, (_, _) => { actionsTriggered[0] = true; }); RegionHandle[] singlePages = new RegionHandle[3]; for (int i = 0; i < 3; i++) { singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); singlePages[i].Reprotect(); } // Write to the second handle. _tracking.VirtualMemoryEvent(PageSize * 9, PageSize, true); // Add an action to the third handle. singlePages[2].RegisterAction((_, _) => { actionsTriggered[1] = true; }); RegionHandle[] doublePages = new RegionHandle[3]; for (int i = 0; i < 3; i++) { doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); doublePages[i].Reprotect(); } // Write to the second handle. _tracking.VirtualMemoryEvent(PageSize * 13, PageSize * 2, true); // Add an action to the third handle. doublePages[2].RegisterAction((_, _) => { actionsTriggered[2] = true; }); // Finally, create a granular handle that inherits all these handles. IEnumerable <IRegionHandle>[] handleGroups = new IEnumerable <IRegionHandle>[] { granular.GetHandles(), singlePages, doublePages }; MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); bool[] expectedDirty = new bool[] { true, true, true, // Gap. false, true, false, // Multi-region. true, true, // Gap. false, true, false, // Individual handles. false, false, true, true, false, false, // Double size handles. true // Gap. }; for (int i = 0; i < 18; i++) { bool modified = false; combined.QueryModified(PageSize * (ulong)i, PageSize, (_, _) => { modified = true; }); Assert.AreEqual(expectedDirty[i], modified); } Assert.AreEqual(new bool[3], actionsTriggered); _tracking.VirtualMemoryEvent(PageSize * 5, PageSize, false); Assert.IsTrue(actionsTriggered[0]); _tracking.VirtualMemoryEvent(PageSize * 10, PageSize, false); Assert.IsTrue(actionsTriggered[1]); _tracking.VirtualMemoryEvent(PageSize * 15, PageSize, false); Assert.IsTrue(actionsTriggered[2]); // The double page handles should be disposed, as they were split into granular handles. foreach (RegionHandle doublePage in doublePages) { // These should have been disposed. bool throws = false; try { doublePage.Dispose(); } catch (ObjectDisposedException) { throws = true; } Assert.IsTrue(throws); } IEnumerable <IRegionHandle> combinedHandles = combined.GetHandles(); Assert.AreEqual(handleGroups[0].ElementAt(0), combinedHandles.ElementAt(3)); Assert.AreEqual(handleGroups[0].ElementAt(1), combinedHandles.ElementAt(4)); Assert.AreEqual(handleGroups[0].ElementAt(2), combinedHandles.ElementAt(5)); Assert.AreEqual(singlePages[0], combinedHandles.ElementAt(8)); Assert.AreEqual(singlePages[1], combinedHandles.ElementAt(9)); Assert.AreEqual(singlePages[2], combinedHandles.ElementAt(10)); }
/// <summary> /// Creates a new region /// </summary> /// <param name="handle"></param> /// <param name="host"></param> /// <param name="gridsPerRegionEdge"></param> /// <param name="gridsPerPatchEdge"></param> /// <param name="regionWidth"> in metres</param> public Region(RegionHandle handle, Host host, UInt32 gridsPerRegionEdge, UInt32 gridsPerPatchEdge, float regionWidth) { Host = host; Handle = handle; TimeDilation = 1.0f; Name = ""; Zoning = ""; IsCurrentPlayerEstateOwner = false; RegionFlags = RegionFlags.Default; RegionProtocols = RegionProtocols.None; SimAccess = SimAccess.Min; BillableFactor = 1.0f; MaxTasks = DEFAULT_MAX_REGION_WIDE_PRIM_COUNT; CentralBakeVersion = 1; CpuClassId = 0; CpuRatio = 0; ColoName = "unknown"; ProductSku = "unknown"; ProductName = "unknown"; ViewerAssetUrl = ""; CacheLoaded = false; CacheDirty = false; ReleaseNotesRequested = false; CapabilitiesReceived = false; SimulatorFeaturesReceived = false; BitsReceived = 0f; PacketsReceived = 0f; Dead = false; // TODO: LastVisitedEntry = null; InvisibilityCheckHistory = 0xffffffff; Paused = false; RegionCacheHitCount = 0; RegionCacheMissCount = 0; Width = regionWidth; OriginGlobal = handle.ToVector3Double(); //TODO: updateRenderMatrix(); Land = new Surface(SurfaceType.Land, null); //TODO: Why not set the region right away? // Create the composition layer for the surface //Composition = new VolumeLayerComposition (Land, gridsPerRegionEdge, regionWidth / gridsPerRegionEdge); //Composition.SetSurface (Land); // Create the surfaces Land.SetRegion(this); Land.Create(gridsPerRegionEdge, gridsPerPatchEdge, OriginGlobal, Width); //TODO: ParcelOverlay = new LLViewerParcelOverlay(this, regionWidth); //TODO: CalculateCenterGlobal(); // Create the object lists // TODO: InitStats(); //TODO: create object partitions //MUST MATCH declaration of eObjectPartitions //ObjectPartition.Add (new LLHUDPartition(this)); //PARTITION_HUD //ObjectPartition.Add (new LLTerrainPartition(this)); //PARTITION_TERRAIN //ObjectPartition.Add (new LLVoidWaterPartition(this)); //PARTITION_VOIDWATER //ObjectPartition.Add (new LLWaterPartition(this)); //PARTITION_WATER //ObjectPartition.Add (new LLTreePartition(this)); //PARTITION_TREE //ObjectPartition.Add (new LLParticlePartition(this)); //PARTITION_PARTICLE //ObjectPartition.Add (new LLGrassPartition(this)); //PARTITION_GRASS //ObjectPartition.Add (new LLVolumePartition(this)); //PARTITION_VOLUME //ObjectPartition.Add (new LLBridgePartition(this)); //PARTITION_BRIDGE //ObjectPartition.Add (new LLAvatarPartition(this)); //PARTITION_AVATAR //ObjectPartition.Add (new LLControlAVPartition(this)); //PARTITION_CONTROL_AV //ObjectPartition.Add (new LLHUDParticlePartition(this));//PARTITION_HUD_PARTICLE //ObjectPartition.Add (new LLVOCachePartition(this)); //PARTITION_VO_CACHE //ObjectPartition.Add (null); //PARTITION_NONE //VOCachePartition = getVOCachePartition(); // TODO: setCapabilitiesReceivedCallback(boost::bind(&LLAvatarRenderInfoAccountant::scanNewRegion, _1)); }
protected override void DeSerialise(byte[] buf, ref int o, int length) { RegionHandle = new RegionHandle(BinarySerializer.DeSerializeUInt64_Le(buf, ref o, length)); TimeDilation = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length); int nObjects = buf[o++]; for (int i = 0; i < nObjects; i++) { int len; ObjectUpdateMessage.ObjectData data = new ObjectUpdateMessage.ObjectData(); Objects.Add(data); data.LocalId = BinarySerializer.DeSerializeUInt32_Le(buf, ref o, length); data.State = buf[o++]; data.FullId = BinarySerializer.DeSerializeGuid(buf, ref o, length); data.Crc = BinarySerializer.DeSerializeUInt32_Le(buf, ref o, length); data.PCode = (PCode)buf[o++]; data.Material = (MaterialType)buf[o++]; data.ClickAction = (ClickAction)buf[o++]; data.Scale = BinarySerializer.DeSerializeVector3(buf, ref o, buf.Length); data.MovementUpdate = DeSerializeMovementUpdate(buf, ref o, buf.Length); data.ParentId = BinarySerializer.DeSerializeUInt32_Le(buf, ref o, length); data.UpdateFlags = (ObjectUpdateFlags)BinarySerializer.DeSerializeUInt32_Le(buf, ref o, length); data.PathCurve = (PathType)buf[o++]; data.ProfileCurve = (ProfileType)buf[o++]; data.PathBegin = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length) * CUT_QUANTA; data.PathEnd = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length) * CUT_QUANTA; data.PathScaleX = buf[o++] * SCALE_QUANTA; data.PathScaleY = buf[o++] * SCALE_QUANTA; data.PathShearX = buf[o++] * SHEAR_QUANTA; data.PathShearY = buf[o++] * SHEAR_QUANTA; data.PathTwist = (sbyte)buf[o++] * SCALE_QUANTA; data.PathTwistBegin = (sbyte)buf[o++] * SCALE_QUANTA; data.PathRadiusOffset = (sbyte)buf[o++] * SCALE_QUANTA; data.PathTaperX = (sbyte)buf[o++] * TAPER_QUANTA; data.PathTaperY = (sbyte)buf[o++] * TAPER_QUANTA; data.PathRevolutions = buf[o++] * REV_QUANTA; data.PathSkew = (sbyte)buf[o++] * SCALE_QUANTA; data.ProfileBegin = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length) * CUT_QUANTA; data.ProfileEnd = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length) * CUT_QUANTA; data.ProfileHollow = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length) * HOLLOW_QUANTA; data.TextureEntry = BinarySerializer.DeSerializeTextureEntry(buf, ref o, length); data.TextureAnimation = BinarySerializer.DeSerializeTextureAnimation(buf, ref o, length); data.NameValue = BinarySerializer.DeSerializeString(buf, ref o, length, 2); len = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length); data.Data2 = new byte[len]; Array.Copy(buf, o, data.Data2, 0, len); o += len; data.Text = BinarySerializer.DeSerializeString(buf, ref o, length, 1); data.TextColour = BinarySerializer.DeSerializeColor(buf, ref o, length); data.MediaUrl = BinarySerializer.DeSerializeString(buf, ref o, length, 1); len = buf[o++]; data.ParticleSystemData = new byte[len]; Array.Copy(buf, o, data.ParticleSystemData, 0, len); o += len; len = buf[o++]; data.ExtraParameters = BinarySerializer.DeSerializeExtraParameters(buf, ref o, o + len); data.SoundId = BinarySerializer.DeSerializeGuid(buf, ref o, length); data.OwnerId = BinarySerializer.DeSerializeGuid(buf, ref o, length); data.Gain = BinarySerializer.DeSerializeUInt32_Le(buf, ref o, buf.Length); data.SoundFlags = (SoundFlags)buf[o++]; data.Radius = BinarySerializer.DeSerializeFloat_Le(buf, ref o, length); data.JointType = (JointType)buf[o++]; data.JointPivot = BinarySerializer.DeSerializeVector3(buf, ref o, buf.Length); data.JointAxisOrAnchor = BinarySerializer.DeSerializeVector3(buf, ref o, buf.Length); //Logger.LogDebug("ObjectUpdateMessage.DeSerialise", ToString()); } }
protected override void DeSerialise(byte[] buf, ref int o, int length) { RegionHandle = new RegionHandle(BinarySerializer.DeSerializeUInt64_Le(buf, ref o, length)); TimeDilation = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length); string logMessage = $"ObjectUpdateCompressed: RegionHandle={RegionHandle}, TimeDilation={TimeDilation}"; int nObjects = buf[o++]; for (int i = 0; i < nObjects; i++) { UInt32 len; ObjectUpdateMessage.ObjectData data = new ObjectUpdateMessage.ObjectData(); Objects.Add(data); data.UpdateFlags = (ObjectUpdateFlags)BinarySerializer.DeSerializeUInt32_Le(buf, ref o, length); int compressedLength = BinarySerializer.DeSerializeUInt16_Le(buf, ref o, length); byte[] compressedData = new byte[compressedLength]; Array.Copy(buf, o, compressedData, 0, compressedLength); o += compressedLength; int compressedOffset = 0; logMessage += $"\n Object {i}: UpdateFlags={data.UpdateFlags}, Data({compressedData.Length})={BitConverter.ToString(compressedData)}"; data.FullId = BinarySerializer.DeSerializeGuid(compressedData, ref compressedOffset, compressedLength); data.LocalId = BinarySerializer.DeSerializeUInt32_Le(compressedData, ref compressedOffset, compressedLength); data.PCode = (PCode)compressedData[compressedOffset++]; data.State = compressedData[compressedOffset++]; data.Crc = BinarySerializer.DeSerializeUInt32_Le(compressedData, ref compressedOffset, compressedLength); data.Material = (MaterialType)compressedData[compressedOffset++]; data.ClickAction = (ClickAction)compressedData[compressedOffset++]; data.Scale = BinarySerializer.DeSerializeVector3(compressedData, ref compressedOffset, compressedLength); data.Position = BinarySerializer.DeSerializeVector3(compressedData, ref compressedOffset, compressedLength); data.Rotation = BinarySerializer.DeSerializeQuaternion(compressedData, ref compressedOffset, compressedLength); CompressedFlags compressedFlags = (CompressedFlags)BinarySerializer.DeSerializeUInt32_Le(compressedData, ref compressedOffset, compressedLength); data.OwnerId = BinarySerializer.DeSerializeGuid(compressedData, ref compressedOffset, compressedLength); logMessage += $"\n FullId={data.FullId}, LocalId={data.LocalId}, PCode={data.PCode}, State={data.State}, Crc={data.Crc}, Material={data.Material}, ClickAction={data.ClickAction}, Scale={data.Scale}, Position={data.Position}, Rotation={data.Rotation}, CompressedFlags=({compressedFlags})"; if ((compressedFlags & CompressedFlags.HasAngularVelocity) != 0) { data.AngularVelocity = BinarySerializer.DeSerializeVector3(compressedData, ref compressedOffset, compressedLength); logMessage += $", AngularVelocity={data.AngularVelocity}"; } data.ParentId = (compressedFlags & CompressedFlags.HasParent) != 0 ? BinarySerializer.DeSerializeUInt32_Le(compressedData, ref compressedOffset, compressedLength) : (uint)0; logMessage += $", ParentId={data.ParentId}"; if ((compressedFlags & CompressedFlags.Tree) != 0) { byte treeSpecies = compressedData[compressedOffset++]; logMessage += $", TreeSpecies={treeSpecies}"; } if ((compressedFlags & CompressedFlags.ScratchPad) != 0) { len = compressedData[compressedOffset++]; compressedOffset += (int)len; // TODO: These offsets and length should all be UInt32 logMessage += $", Scratchpad({len})"; } if ((compressedFlags & CompressedFlags.HasText) != 0) { data.Text = BinarySerializer.DeSerializeString(compressedData, ref compressedOffset, compressedLength, 0); data.TextColour = BinarySerializer.DeSerializeColor(compressedData, ref compressedOffset, compressedLength); logMessage += $", Text={data.Text}, TextColour={data.TextColour}"; } if ((compressedFlags & CompressedFlags.MediaURL) != 0) { data.MediaUrl = BinarySerializer.DeSerializeString(compressedData, ref compressedOffset, compressedLength, 0); logMessage += $", MediaUrl={data.MediaUrl}"; } if ((compressedFlags & CompressedFlags.HasParticles) != 0) { // TODO: Parse the particle system data. OpenMetaverse says that this is a BitPack of 86 bytes. len = 86; compressedOffset += (int)len; logMessage += $", ParticleSystem({len})"; } data.ExtraParameters = BinarySerializer.DeSerializeExtraParameters(compressedData, ref compressedOffset, compressedOffset + compressedLength); if ((compressedFlags & CompressedFlags.HasSound) != 0) { data.SoundId = BinarySerializer.DeSerializeGuid(compressedData, ref compressedOffset, compressedLength); data.Gain = BinarySerializer.DeSerializeUInt32_Le(compressedData, ref compressedOffset, compressedLength); data.SoundFlags = (SoundFlags)compressedData[compressedOffset++]; data.Radius = BinarySerializer.DeSerializeFloat_Le(compressedData, ref compressedOffset, compressedLength); logMessage += $", SoundId={data.SoundId}, Gain={data.Gain}, SoundFlags={data.SoundFlags}, Radius={data.Radius}"; } if ((compressedFlags & CompressedFlags.HasNameValues) != 0) { data.NameValue = BinarySerializer.DeSerializeString(compressedData, ref compressedOffset, compressedLength, 0); logMessage += $", NameValue={data.NameValue}"; } data.PathCurve = (PathType)compressedData[compressedOffset++]; data.PathBegin = BinarySerializer.DeSerializeUInt16_Le(compressedData, ref compressedOffset, length) * CUT_QUANTA; data.PathEnd = BinarySerializer.DeSerializeUInt16_Le(compressedData, ref compressedOffset, length) * CUT_QUANTA; data.PathScaleX = compressedData[compressedOffset++] * SCALE_QUANTA; data.PathScaleY = compressedData[compressedOffset++] * SCALE_QUANTA; data.PathShearX = compressedData[compressedOffset++] * SHEAR_QUANTA; data.PathShearY = compressedData[compressedOffset++] * SHEAR_QUANTA; data.PathTwist = (sbyte)compressedData[compressedOffset++] * SCALE_QUANTA; data.PathTwistBegin = (sbyte)compressedData[compressedOffset++] * SCALE_QUANTA; data.PathRadiusOffset = (sbyte)compressedData[compressedOffset++] * SCALE_QUANTA; data.PathTaperX = (sbyte)compressedData[compressedOffset++] * TAPER_QUANTA; data.PathTaperY = (sbyte)compressedData[compressedOffset++] * TAPER_QUANTA; data.PathRevolutions = compressedData[compressedOffset++] * REV_QUANTA; data.PathSkew = (sbyte)compressedData[compressedOffset++] * SCALE_QUANTA; data.ProfileCurve = (ProfileType)compressedData[compressedOffset++]; data.ProfileBegin = BinarySerializer.DeSerializeUInt16_Le(compressedData, ref compressedOffset, length) * CUT_QUANTA; data.ProfileEnd = BinarySerializer.DeSerializeUInt16_Le(compressedData, ref compressedOffset, length) * CUT_QUANTA; data.ProfileHollow = BinarySerializer.DeSerializeUInt16_Le(compressedData, ref compressedOffset, length) * HOLLOW_QUANTA; data.TextureEntry = BinarySerializer.DeSerializeTextureEntry(compressedData, ref compressedOffset, compressedLength, true); logMessage += $", TextureEntry={data.TextureEntry}"; if ((compressedFlags & CompressedFlags.TextureAnimation) != 0) { data.TextureAnimation = BinarySerializer.DeSerializeTextureAnimation(compressedData, ref compressedOffset, compressedLength); logMessage += ", TextureAnimation"; } data.IsAttachment = (compressedFlags & CompressedFlags.HasNameValues) != 0 && data.ParentId != 0; } //Logger.LogDebug("ObjectUpdateCompressedMessage.DeSerialise", logMessage); }
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(); }