aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/Common/Counter.cs
blob: 6db9561ca4ac5b1e79976fa36fb81dda4ce2c61b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System;

namespace ARMeilleure.Common
{
    /// <summary>
    /// Represents a numeric counter which can be used for instrumentation of compiled code.
    /// </summary>
    /// <typeparam name="T">Type of the counter</typeparam>
    class Counter<T> : IDisposable where T : unmanaged
    {
        private bool _disposed;
        /// <summary>
        /// Index in the <see cref="EntryTable{T}"/>
        /// </summary>
        private readonly int _index;
        private readonly EntryTable<T> _countTable;

        /// <summary>
        /// Initializes a new instance of the <see cref="Counter{T}"/> class from the specified
        /// <see cref="EntryTable{T}"/> instance and index.
        /// </summary>
        /// <param name="countTable"><see cref="EntryTable{T}"/> instance</param>
        /// <exception cref="ArgumentNullException"><paramref name="countTable"/> is <see langword="null"/></exception>
        /// <exception cref="ArgumentException"><typeparamref name="T"/> is unsupported</exception>
        public Counter(EntryTable<T> countTable)
        {
            if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) &&
                typeof(T) != typeof(short) && typeof(T) != typeof(ushort) &&
                typeof(T) != typeof(int) && typeof(T) != typeof(uint) &&
                typeof(T) != typeof(long) && typeof(T) != typeof(ulong) &&
                typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) &&
                typeof(T) != typeof(float) && typeof(T) != typeof(double))
            {
                throw new ArgumentException("Counter does not support the specified type.");
            }

            _countTable = countTable ?? throw new ArgumentNullException(nameof(countTable));
            _index = countTable.Allocate();
        }

        /// <summary>
        /// Gets a reference to the value of the counter.
        /// </summary>
        /// <exception cref="ObjectDisposedException"><see cref="Counter{T}"/> instance was disposed</exception>
        /// <remarks>
        /// This can refer to freed memory if the owning <see cref="EntryTable{TEntry}"/> is disposed.
        /// </remarks>
        public ref T Value
        {
            get
            {
                ObjectDisposedException.ThrowIf(_disposed, this);

                return ref _countTable.GetValue(_index);
            }
        }

        /// <summary>
        /// Releases all resources used by the <see cref="Counter{T}"/> instance.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases all unmanaged and optionally managed resources used by the <see cref="Counter{T}"/> instance.
        /// </summary>
        /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resources</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                try
                {
                    // The index into the EntryTable is essentially an unmanaged resource since we allocate and free the
                    // resource ourselves.
                    _countTable.Free(_index);
                }
                catch (ObjectDisposedException)
                {
                    // Can happen because _countTable may be disposed before the Counter instance.
                }

                _disposed = true;
            }
        }

        /// <summary>
        /// Frees resources used by the <see cref="Counter{T}"/> instance.
        /// </summary>
        ~Counter()
        {
            Dispose(false);
        }
    }
}