A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Collections.Generic;
6using System.Threading;
7using System.Threading.Tasks;
8using osu.Framework.Statistics;
9
10namespace osu.Framework.Allocation
11{
12 /// <summary>
13 /// A queue which batches object disposal on threadpool threads.
14 /// </summary>
15 internal static class AsyncDisposalQueue
16 {
17 private static readonly GlobalStatistic<string> last_disposal = GlobalStatistics.Get<string>("Drawable", "Last disposal");
18
19 private static readonly List<IDisposable> disposal_queue = new List<IDisposable>();
20
21 private static Task runTask;
22
23 private static readonly ManualResetEventSlim processing_reset_event = new ManualResetEventSlim(true);
24
25 private static int runningTasks;
26
27 /// <summary>
28 /// Enqueue a disposable object for asynchronous disposal.
29 /// </summary>
30 /// <param name="disposable">The object to dispose.</param>
31 public static void Enqueue(IDisposable disposable)
32 {
33 lock (disposal_queue)
34 {
35 disposal_queue.Add(disposable);
36
37 if (runTask?.Status < TaskStatus.Running)
38 return;
39
40 Interlocked.Increment(ref runningTasks);
41 processing_reset_event.Reset();
42 }
43
44 runTask = Task.Run(() =>
45 {
46 try
47 {
48 IDisposable[] itemsToDispose;
49
50 lock (disposal_queue)
51 {
52 itemsToDispose = disposal_queue.ToArray();
53 disposal_queue.Clear();
54 }
55
56 for (int i = 0; i < itemsToDispose.Length; i++)
57 {
58 ref var item = ref itemsToDispose[i];
59
60 last_disposal.Value = item.ToString();
61 item.Dispose();
62
63 item = null;
64 }
65 }
66 finally
67 {
68 lock (disposal_queue)
69 {
70 if (Interlocked.Decrement(ref runningTasks) == 0)
71 processing_reset_event.Set();
72 }
73 }
74 });
75 }
76
77 /// <summary>
78 /// Wait until all items in the async disposal queue have been flushed.
79 /// Will wait for a maximum of 10 seconds.
80 /// </summary>
81 public static void WaitForEmpty() => processing_reset_event.Wait(TimeSpan.FromSeconds(10));
82 }
83}