Exemple #1
0
        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);
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        public void TestManyAsyncDisposalUsingWait()
        {
            var objects = new List <DisposableObject>();

            for (int i = 0; i < 10000; i++)
            {
                objects.Add(new DisposableObject());
            }

            objects.ForEach(AsyncDisposalQueue.Enqueue);

            AsyncDisposalQueue.WaitForEmpty();

            Assert.That(objects.All(o => o.IsDisposed));
        }
Exemple #4
0
        protected override void Dispose(bool isDisposing)
        {
            // ensure any async disposals are completed before we begin to rip components out.
            // if we were to not wait, async disposals may throw unexpected exceptions.
            AsyncDisposalQueue.WaitForEmpty();

            base.Dispose(isDisposing);

            // call a second time to protect against anything being potentially async disposed in the base.Dispose call.
            AsyncDisposalQueue.WaitForEmpty();

            Audio?.Dispose();
            Audio = null;

            Fonts?.Dispose();
            Fonts = null;

            localFonts?.Dispose();
            localFonts = null;
        }
Exemple #5
0
 public override void Dispose()
 {
     base.Dispose();
     AsyncDisposalQueue.Enqueue(Nested = new DisposableObject());
 }