public void MultipleReleaseAtDifferentThreadTest() { ThreadLocalPool <HandledObject> pool = NewPool(1024); HandledObject obj = pool.Take(); Thread thread = new Thread(() => { obj.Release(); }); thread.Start(); thread.Join(); ExceptionDispatchInfo exceptionDispatchInfo = null; Thread thread2 = new Thread(() => { try { obj.Release(); } catch (Exception ex) { Interlocked.Exchange(ref exceptionDispatchInfo, ExceptionDispatchInfo.Capture(ex)); } }); thread2.Start(); thread2.Join(); var exception = Assert.ThrowsAny <InvalidOperationException>(() => Volatile.Read(ref exceptionDispatchInfo)?.Throw()); Assert.True(exception != null); }
public void MultipleReleaseTest() { ThreadLocalPool <HandledObject> pool = NewPool(1024); HandledObject obj = pool.Take(); obj.Release(); var exception = Assert.ThrowsAny <InvalidOperationException>(() => obj.Release()); Assert.True(exception != null); }
public void ReleaseDisableTest() { ThreadLocalPool <HandledObject> pool = NewPool(-1); HandledObject obj = pool.Take(); obj.Release(); HandledObject obj2 = pool.Take(); Assert.NotSame(obj, obj2); obj2.Release(); }
public void DiscardingExceedingElementsWithReleaseAtDifferentThreadTest() { int maxCapacity = 32; int instancesCount = 0; ThreadLocalPool <HandledObject> pool = new ThreadLocalPool <HandledObject>(handle => { Interlocked.Increment(ref instancesCount); return(new HandledObject(handle)); }, maxCapacity, 2); // Borrow 2 * maxCapacity objects. HandledObject[] array = new HandledObject[maxCapacity * 2]; for (int i = 0; i < array.Length; i++) { array[i] = pool.Take(); } Assert.Equal(array.Length, Volatile.Read(ref instancesCount)); // Reset counter. Interlocked.Exchange(ref instancesCount, 0); // Release from other thread. Thread thread = new Thread(() => { for (int i = 0; i < array.Length; i++) { array[i].Release(); } }); thread.Start(); thread.Join(); Assert.Equal(0, Volatile.Read(ref instancesCount)); // Borrow 2 * maxCapacity objects. Half of them should come from // the recycler queue, the other half should be freshly allocated. for (int i = 0; i < array.Length; i++) { array[i] = pool.Take(); } // The implementation uses maxCapacity / 2 as limit per WeakOrderQueue Assert.True(array.Length - maxCapacity / 2 <= Volatile.Read(ref instancesCount), "The instances count (" + Volatile.Read(ref instancesCount) + ") must be <= array.length (" + array.Length + ") - maxCapacity (" + maxCapacity + ") / 2 as we not pool all new handles" + " internally"); }
public void ThreadCanBeCollectedEvenIfHandledObjectIsReferencedTest() { ThreadLocalPool <HandledObject> pool = NewPool(1024); HandledObject reference = null; WeakReference <Thread> threadRef = null; WeakReference <XThread> xThreadRef = null; var thread1 = new Thread(() => { //Don't know the reason, but thread2 will not be collected without wrapped with thread1 var thread2 = new Thread(() => { Interlocked.Exchange(ref xThreadRef, new WeakReference <XThread>(XThread.CurrentThread)); HandledObject data = pool.Take(); // Store a reference to the HandledObject to ensure it is not collected when the run method finish. Interlocked.Exchange(ref reference, data); }); Interlocked.Exchange(ref threadRef, new WeakReference <Thread>(thread2)); thread2.Start(); thread2.Join(); Assert.True(Volatile.Read(ref threadRef)?.TryGetTarget(out _)); Assert.True(Volatile.Read(ref xThreadRef)?.TryGetTarget(out _)); GC.KeepAlive(thread2); // Null out so it can be collected. thread2 = null; }); thread1.Start(); thread1.Join(); for (int i = 0; i < 5; ++i) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); GC.WaitForPendingFinalizers(); if (Volatile.Read(ref threadRef)?.TryGetTarget(out _) == true || Volatile.Read(ref xThreadRef)?.TryGetTarget(out _) == true) { Thread.Sleep(100); } } Assert.False(Volatile.Read(ref threadRef)?.TryGetTarget(out _)); Assert.False(Volatile.Read(ref xThreadRef)?.TryGetTarget(out _)); // Now call recycle after the Thread was collected to ensure this still works... reference.Release(); reference = null; }
public void RecycleDisableDrop() { ThreadLocalPool <HandledObject> recycler = NewPool(1024, 2, 0, 2, 0); HandledObject obj = recycler.Take(); obj.Release(); HandledObject obj2 = recycler.Take(); Assert.Same(obj, obj2); obj2.Release(); HandledObject obj3 = recycler.Take(); Assert.Same(obj, obj3); obj3.Release(); }
public void RecycleDisableDelayedQueueDrop() { ThreadLocalPool <HandledObject> recycler = NewPool(1024, 2, 1, 2, 0); HandledObject o = recycler.Take(); HandledObject o2 = recycler.Take(); HandledObject o3 = recycler.Take(); Task.Run(() => { o.Release(); o2.Release(); o3.Release(); }).Wait(); // In reverse order Assert.Same(o3, recycler.Take()); Assert.Same(o, recycler.Take()); }
public void ReleaseAtDifferentThreadTest() { ThreadLocalPool <HandledObject> pool = NewPool(256, 10, 2, 10, 2); HandledObject obj = pool.Take(); HandledObject obj2 = pool.Take(); Thread thread = new Thread(() => { obj.Release(); obj2.Release(); }); thread.Start(); thread.Join(); Assert.Same(pool.Take(), obj); Assert.NotSame(pool.Take(), obj2); }
void MaxCapacityTest0(int maxCapacity) { var recycler = new ThreadLocalPool <HandledObject>(handle => new HandledObject(handle), maxCapacity); var objects = new HandledObject[maxCapacity * 3]; for (int i = 0; i < objects.Length; i++) { objects[i] = recycler.Take(); } for (int i = 0; i < objects.Length; i++) { objects[i].Release(); objects[i] = null; } Assert.Equal(maxCapacity, recycler.ThreadLocalCapacity); }
public void MaxCapacityWithReleaseAtDifferentThreadTest() { const int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY var pool = NewPool(maxCapacity); // Borrow 2 * maxCapacity objects. // Return the half from the same thread. // Return the other half from the different thread. var array = new HandledObject[maxCapacity * 3]; for (int i = 0; i < array.Length; i++) { array[i] = pool.Take(); } for (int i = 0; i < maxCapacity; i++) { array[i].Release(); } Thread thread = new Thread(() => { for (int i = maxCapacity; i < array.Length; i++) { array[i].Release(); } }); thread.Start(); thread.Join(); Assert.Equal(maxCapacity, pool.ThreadLocalCapacity); Assert.Equal(1, pool.ThreadLocalSize); for (int i = 0; i < array.Length; i++) { pool.Take(); } Assert.Equal(maxCapacity, pool.ThreadLocalCapacity); Assert.Equal(0, pool.ThreadLocalSize); }
static void MaxCapacityTest0(int maxCapacity) { var recycler = NewPool(maxCapacity); var objects = new HandledObject[maxCapacity * 3]; for (int i = 0; i < objects.Length; i++) { objects[i] = recycler.Take(); } for (int i = 0; i < objects.Length; i++) { objects[i].Release(); objects[i] = null; } Assert.True(maxCapacity >= recycler.ThreadLocalCapacity, "The threadLocalCapacity (" + recycler.ThreadLocalCapacity + ") must be <= maxCapacity (" + maxCapacity + ") as we not pool all new handles internally"); }
public void MaxCapacityWithRecycleAtDifferentThreadTest() { const int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY var recycler = new ThreadLocalPool <HandledObject>(handle => new HandledObject(handle), maxCapacity); // Borrow 2 * maxCapacity objects. // Return the half from the same thread. // Return the other half from the different thread. var array = new HandledObject[maxCapacity * 3]; for (int i = 0; i < array.Length; i++) { array[i] = recycler.Take(); } for (int i = 0; i < maxCapacity; i++) { array[i].Release(); } Task.Run(() => { for (int i = maxCapacity; i < array.Length; i++) { array[i].Release(); } }).Wait(); Assert.Equal(recycler.ThreadLocalCapacity, maxCapacity); Assert.Equal(recycler.ThreadLocalSize, maxCapacity); for (int i = 0; i < array.Length; i++) { recycler.Take(); } Assert.Equal(maxCapacity, recycler.ThreadLocalCapacity); Assert.Equal(0, recycler.ThreadLocalSize); }
/// <inheritdoc/> protected internal override void PostInput() { if (activeHandle != null) { if (activeHandle.IsDragged()) { if (!isDragged) { isDragged = true; SceneObject[] selectedSceneObjects = Selection.SceneObjects; activeSelection = new HandledObject[selectedSceneObjects.Length]; for (int i = 0; i < selectedSceneObjects.Length; i++) activeSelection[i] = new HandledObject(selectedSceneObjects[i]); initialHandlePosition = activeHandle.Position; initialHandleRotation = activeHandle.Rotation; } } else { isDragged = false; activeSelection = null; } activeHandle.PostInput(); if (activeHandle.IsDragged()) { switch (activeHandleType) { case SceneViewTool.Move: MoveHandle moveHandle = (MoveHandle) activeHandle; foreach (var selectedObj in activeSelection) selectedObj.so.LocalPosition = selectedObj.initialPosition + moveHandle.Delta; break; case SceneViewTool.Rotate: { RotateHandle rotateHandle = (RotateHandle) activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = initialHandlePosition; temporarySO.LocalRotation = initialHandleRotation; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalRotation *= rotateHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) activeSelection[i].so.Parent = originalParents[i]; temporarySO.Destroy(); } break; case SceneViewTool.Scale: { ScaleHandle scaleHandle = (ScaleHandle) activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = activeHandle.Position; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.LocalScale = activeSelection[i].initialScale; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalScale += scaleHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) activeSelection[i].so.Parent = originalParents[i]; temporarySO.Destroy(); } break; } EditorApplication.SetSceneDirty(); } } else { isDragged = false; activeSelection = null; } }
/// <inheritdoc/> protected internal override void PostInput() { if (activeHandle != null) { if (activeHandle.IsDragged()) { if (!isDragged) { isDragged = true; SceneObject[] selectedSceneObjects = Selection.SceneObjects; activeSelection = new HandledObject[selectedSceneObjects.Length]; for (int i = 0; i < selectedSceneObjects.Length; i++) { activeSelection[i] = new HandledObject(selectedSceneObjects[i]); } initialHandlePosition = activeHandle.Position; initialHandleRotation = activeHandle.Rotation; } } else { isDragged = false; activeSelection = null; } activeHandle.PostInput(); if (activeHandle.IsDragged()) { switch (activeHandleType) { case SceneViewTool.Move: MoveHandle moveHandle = (MoveHandle)activeHandle; foreach (var selectedObj in activeSelection) { selectedObj.so.LocalPosition = selectedObj.initialPosition + moveHandle.Delta; } break; case SceneViewTool.Rotate: { RotateHandle rotateHandle = (RotateHandle)activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = initialHandlePosition; temporarySO.LocalRotation = initialHandleRotation; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalRotation *= rotateHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) { activeSelection[i].so.Parent = originalParents[i]; } temporarySO.Destroy(); } break; case SceneViewTool.Scale: { ScaleHandle scaleHandle = (ScaleHandle)activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = activeHandle.Position; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.LocalScale = activeSelection[i].initialScale; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalScale += scaleHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) { activeSelection[i].so.Parent = originalParents[i]; } temporarySO.Destroy(); } break; } EditorApplication.SetSceneDirty(); } } else { isDragged = false; activeSelection = null; } }
public void MaxCapacityWithRecycleAtDifferentThreadTest() { const int maxCapacity = 4; // Choose the number smaller than WeakOrderQueue.LINK_CAPACITY var recycler = new ThreadLocalPool<HandledObject>(handle => new HandledObject(handle), maxCapacity); // Borrow 2 * maxCapacity objects. // Return the half from the same thread. // Return the other half from the different thread. var array = new HandledObject[maxCapacity * 3]; for (int i = 0; i < array.Length; i++) { array[i] = recycler.Take(); } for (int i = 0; i < maxCapacity; i++) { array[i].Release(); } Task.Run(() => { for (int i = maxCapacity; i < array.Length; i++) { array[i].Release(); } }).Wait(); Assert.Equal(recycler.ThreadLocalCapacity, maxCapacity); Assert.Equal(recycler.ThreadLocalSize, maxCapacity); for (int i = 0; i < array.Length; i++) { recycler.Take(); } Assert.Equal(maxCapacity, recycler.ThreadLocalCapacity); Assert.Equal(0, recycler.ThreadLocalSize); }
void MaxCapacityTest(int maxCapacity) { var recycler = new ThreadLocalPool<HandledObject>(handle => new HandledObject(handle), maxCapacity); var objects = new HandledObject[maxCapacity * 3]; for (int i = 0; i < objects.Length; i++) { objects[i] = recycler.Take(); } for (int i = 0; i < objects.Length; i++) { objects[i].Release(); objects[i] = null; } Assert.Equal(maxCapacity, recycler.ThreadLocalCapacity); }
/// <inheritdoc/> protected internal override void PostInput() { if (activeHandle != null) { if (activeHandle.IsDragged()) { if (!isDragged) { isDragged = true; SceneObject[] selectedSceneObjects = Selection.SceneObjects; GameObjectUndo.RecordSceneObjectHeader(selectedSceneObjects); activeSelection = new HandledObject[selectedSceneObjects.Length]; for (int i = 0; i < selectedSceneObjects.Length; i++) { activeSelection[i] = new HandledObject(selectedSceneObjects[i]); } initialHandlePosition = activeHandle.Position; initialHandleRotation = activeHandle.Rotation; } } else { if (isDragged) { GameObjectUndo.ResolveDiffs(); } isDragged = false; activeSelection = null; } activeHandle.PostInput(); if (activeHandle.IsDragged()) { switch (activeHandleType) { case SceneViewTool.Move: MoveHandle moveHandle = (MoveHandle)activeHandle; foreach (var selectedObj in activeSelection) { SceneObject parentSO = selectedObj.so.Parent; if (parentSO == null) { selectedObj.so.LocalPosition = selectedObj.initialPosition + moveHandle.Delta; } else { Vector3 parentRelativeDelta = parentSO.Rotation.Inverse.Rotate(moveHandle.Delta); selectedObj.so.LocalPosition = selectedObj.initialPosition + parentRelativeDelta; } } break; case SceneViewTool.Rotate: { RotateHandle rotateHandle = (RotateHandle)activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = initialHandlePosition; temporarySO.LocalRotation = initialHandleRotation; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalRotation *= rotateHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) { activeSelection[i].so.Parent = originalParents[i]; } temporarySO.Destroy(); } break; case SceneViewTool.Scale: { ScaleHandle scaleHandle = (ScaleHandle)activeHandle; Vector3 origin = activeHandle.Position; for (int i = 0; i < activeSelection.Length; i++) { Vector3 posDiff = activeSelection[i].initialPosition - origin; Vector3 scale = activeSelection[i].initialScale; Vector3 newScale = scale + scaleHandle.Delta; Vector3 pctScale = new Vector3( newScale.x / scale.x, newScale.y / scale.y, newScale.z / scale.z); activeSelection[i].so.LocalScale = newScale; activeSelection[i].so.Position = origin + posDiff * pctScale; } } break; } SceneObject[] selectedSceneObjects = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { selectedSceneObjects[i] = activeSelection[i].so; } // Make sure to update handle positions for the drawing method (otherwise they lag one frame) UpdateActiveHandleTransform(selectedSceneObjects); EditorApplication.SetSceneDirty(); } } else { isDragged = false; activeSelection = null; } }