/ src / ARMeilleure / Common / Counter.cs
Counter.cs
 1  using System;
 2  
 3  namespace ARMeilleure.Common
 4  {
 5      /// <summary>
 6      /// Represents a numeric counter which can be used for instrumentation of compiled code.
 7      /// </summary>
 8      /// <typeparam name="T">Type of the counter</typeparam>
 9      class Counter<T> : IDisposable where T : unmanaged
10      {
11          private bool _disposed;
12          /// <summary>
13          /// Index in the <see cref="EntryTable{T}"/>
14          /// </summary>
15          private readonly int _index;
16          private readonly EntryTable<T> _countTable;
17  
18          /// <summary>
19          /// Initializes a new instance of the <see cref="Counter{T}"/> class from the specified
20          /// <see cref="EntryTable{T}"/> instance and index.
21          /// </summary>
22          /// <param name="countTable"><see cref="EntryTable{T}"/> instance</param>
23          /// <exception cref="ArgumentNullException"><paramref name="countTable"/> is <see langword="null"/></exception>
24          /// <exception cref="ArgumentException"><typeparamref name="T"/> is unsupported</exception>
25          public Counter(EntryTable<T> countTable)
26          {
27              if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) &&
28                  typeof(T) != typeof(short) && typeof(T) != typeof(ushort) &&
29                  typeof(T) != typeof(int) && typeof(T) != typeof(uint) &&
30                  typeof(T) != typeof(long) && typeof(T) != typeof(ulong) &&
31                  typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) &&
32                  typeof(T) != typeof(float) && typeof(T) != typeof(double))
33              {
34                  throw new ArgumentException("Counter does not support the specified type.");
35              }
36  
37              _countTable = countTable ?? throw new ArgumentNullException(nameof(countTable));
38              _index = countTable.Allocate();
39          }
40  
41          /// <summary>
42          /// Gets a reference to the value of the counter.
43          /// </summary>
44          /// <exception cref="ObjectDisposedException"><see cref="Counter{T}"/> instance was disposed</exception>
45          /// <remarks>
46          /// This can refer to freed memory if the owning <see cref="EntryTable{TEntry}"/> is disposed.
47          /// </remarks>
48          public ref T Value
49          {
50              get
51              {
52                  ObjectDisposedException.ThrowIf(_disposed, this);
53  
54                  return ref _countTable.GetValue(_index);
55              }
56          }
57  
58          /// <summary>
59          /// Releases all resources used by the <see cref="Counter{T}"/> instance.
60          /// </summary>
61          public void Dispose()
62          {
63              Dispose(true);
64              GC.SuppressFinalize(this);
65          }
66  
67          /// <summary>
68          /// Releases all unmanaged and optionally managed resources used by the <see cref="Counter{T}"/> instance.
69          /// </summary>
70          /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resources</param>
71          protected virtual void Dispose(bool disposing)
72          {
73              if (!_disposed)
74              {
75                  try
76                  {
77                      // The index into the EntryTable is essentially an unmanaged resource since we allocate and free the
78                      // resource ourselves.
79                      _countTable.Free(_index);
80                  }
81                  catch (ObjectDisposedException)
82                  {
83                      // Can happen because _countTable may be disposed before the Counter instance.
84                  }
85  
86                  _disposed = true;
87              }
88          }
89  
90          /// <summary>
91          /// Frees resources used by the <see cref="Counter{T}"/> instance.
92          /// </summary>
93          ~Counter()
94          {
95              Dispose(false);
96          }
97      }
98  }