// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using osu.Framework.Statistics; namespace osu.Framework.Allocation { /// /// A queue which batches object disposal on threadpool threads. /// internal static class AsyncDisposalQueue { private static readonly GlobalStatistic last_disposal = GlobalStatistics.Get("Drawable", "Last disposal"); private static readonly List disposal_queue = new List(); private static Task runTask; private static readonly ManualResetEventSlim processing_reset_event = new ManualResetEventSlim(true); private static int runningTasks; /// /// Enqueue a disposable object for asynchronous disposal. /// /// The object to dispose. public static void Enqueue(IDisposable disposable) { lock (disposal_queue) { disposal_queue.Add(disposable); if (runTask?.Status < TaskStatus.Running) return; Interlocked.Increment(ref runningTasks); processing_reset_event.Reset(); } runTask = Task.Run(() => { try { IDisposable[] itemsToDispose; lock (disposal_queue) { itemsToDispose = disposal_queue.ToArray(); disposal_queue.Clear(); } for (int i = 0; i < itemsToDispose.Length; i++) { ref var item = ref itemsToDispose[i]; last_disposal.Value = item.ToString(); item.Dispose(); item = null; } } finally { lock (disposal_queue) { if (Interlocked.Decrement(ref runningTasks) == 0) processing_reset_event.Set(); } } }); } /// /// Wait until all items in the async disposal queue have been flushed. /// Will wait for a maximum of 10 seconds. /// public static void WaitForEmpty() => processing_reset_event.Wait(TimeSpan.FromSeconds(10)); } }