private void performTest(Drawable child) { Container parentContainer = null; var drawableRefs = new List <WeakReference>(); // Add the children to the hierarchy, and build weak-reference wrappers around them AddStep("create hierarchy", () => { drawableRefs.Clear(); buildReferencesRecursive(child); Child = parentContainer = new NonFlattenedContainer { Size = new Vector2(200), Child = child }; void buildReferencesRecursive(Drawable target) { drawableRefs.Add(new WeakReference(target)); if (target is CompositeDrawable compositeTarget) { foreach (var c in compositeTarget.InternalChildren) { buildReferencesRecursive(c); } } } }); AddWaitStep("wait for some draw nodes", GLWrapper.MAX_DRAW_NODES); // Clear the parent to ensure no references are held via drawables themselves, // and remove the parent to ensure that the parent maintains references to the child draw nodes AddStep("clear + remove parent container", () => { parentContainer.Clear(); Remove(parentContainer); // Lose last hard-reference to the child child = null; }); // Wait for all drawables to get disposed DisposalMarker disposalMarker = null; AddStep("add disposal marker", () => AsyncDisposalQueue.Enqueue(disposalMarker = new DisposalMarker())); AddUntilStep("wait for drawables to dispose", () => disposalMarker.Disposed); // Induce the collection of drawables AddStep("invoke GC", () => { GC.Collect(); GC.WaitForPendingFinalizers(); }); AddUntilStep("all drawable references lost", () => !drawableRefs.Any(r => r.IsAlive)); }
public void TestAsyncLoadClearWhileAsyncDisposing() { Container safeContainer = null; DelayedLoadDrawable drawable = null; // We are testing a disposal deadlock scenario. When the test runner exits, it will attempt to dispose the game hierarchy, // and will fall into the deadlocked state itself. For this reason an intermediate "safe" container is used, which is // removed from the hierarchy immediately after use and is thus not disposed when the test runner exits. // This does NOT free up the LoadComponentAsync thread pool for use by other tests - that thread is in a deadlocked state forever. AddStep("add safe container", () => Add(safeContainer = new Container())); // Get the drawable into an async loading state AddStep("begin async load", () => { safeContainer.LoadComponentAsync(drawable = new DelayedLoadDrawable(), _ => { }); Remove(safeContainer); }); AddUntilStep("wait until loading", () => drawable.LoadState == LoadState.Loading); // Make the async disposal queue attempt to dispose the drawable AddStep("enqueue async disposal", () => AsyncDisposalQueue.Enqueue(drawable)); AddWaitStep("wait for disposal task to run", 10); // Clear the contents of the drawable, causing a second async disposal AddStep("allow load", () => drawable.AllowLoad.Set()); AddUntilStep("drawable was cleared successfully", () => drawable.HasCleared); }
public override void Dispose() { base.Dispose(); AsyncDisposalQueue.Enqueue(Nested = new DisposableObject()); }