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 }