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 osu.Framework.Allocation;
6using osu.Framework.Statistics;
7
8namespace osu.Framework.Platform
9{
10 /// <summary>
11 /// Track native memory allocations via <see cref="GlobalStatistics"/>.
12 /// Also adds memory pressure automatically.
13 /// </summary>
14 public static class NativeMemoryTracker
15 {
16 /// <summary>
17 /// Add new tracked native memory.
18 /// </summary>
19 /// <param name="source">The object responsible for this allocation.</param>
20 /// <param name="amount">The number of bytes allocated.</param>
21 /// <returns>A lease which should be disposed when memory is released.</returns>
22 public static NativeMemoryLease AddMemory(object source, long amount)
23 {
24 getStatistic(source).Value += amount;
25 GC.AddMemoryPressure(amount);
26
27 return new NativeMemoryLease((source, amount), sender => removeMemory(sender.source, sender.amount));
28 }
29
30 /// <summary>
31 /// Remove previously tracked native memory.
32 /// </summary>
33 /// <param name="source">The object responsible for this allocation.</param>
34 /// <param name="amount">The number of bytes allocated.</param>
35 private static void removeMemory(object source, long amount)
36 {
37 getStatistic(source).Value -= amount;
38 GC.RemoveMemoryPressure(amount);
39 }
40
41 private static GlobalStatistic<long> getStatistic(object source) => GlobalStatistics.Get<long>("Native", source.GetType().Name);
42
43 /// <summary>
44 /// A leased on a native memory allocation. Should be disposed when the associated memory is freed.
45 /// </summary>
46 public class NativeMemoryLease : InvokeOnDisposal<(object source, long amount)>
47 {
48 internal NativeMemoryLease((object source, long amount) sender, Action<(object source, long amount)> action)
49 : base(sender, action)
50 {
51 }
52
53 private bool isDisposed;
54
55 public override void Dispose()
56 {
57 if (isDisposed)
58 throw new ObjectDisposedException(ToString(), $"{nameof(NativeMemoryLease)} should not be disposed more than once");
59
60 base.Dispose();
61 isDisposed = true;
62 }
63 }
64 }
65}