using System.Diagnostics; using System.Threading; namespace Dalamud.Utility; /// /// Interface for reference counting. /// internal interface IRefCountable : IDisposable { /// /// Result for . /// public enum RefCountResult { /// /// The object still has remaining references. No futher action should be done. /// StillAlive = 1, /// /// The last reference to the object has been released. The object should be fully released. /// FinalRelease = 2, /// /// The object already has been disposed. may be thrown. /// AlreadyDisposed = 3, } /// /// Adds a reference to this reference counted object. /// /// The new number of references. int AddRef(); /// /// Releases a reference from this reference counted object.
/// When all references are released, the object will be fully disposed. ///
/// The new number of references. int Release(); /// /// Alias for . /// void IDisposable.Dispose() => this.Release(); /// /// Alters by . /// /// The delta to the reference count. /// The reference to the reference count. /// The new reference count. /// The followup action that should be done. public static RefCountResult AlterRefCount(int delta, ref int refCount, out int newRefCount) { Debug.Assert(delta is 1 or -1, "delta must be 1 or -1"); while (true) { var refCountCopy = refCount; if (refCountCopy <= 0) { newRefCount = refCountCopy; return RefCountResult.AlreadyDisposed; } newRefCount = refCountCopy + delta; if (refCountCopy != Interlocked.CompareExchange(ref refCount, newRefCount, refCountCopy)) continue; return newRefCount == 0 ? RefCountResult.FinalRelease : RefCountResult.StillAlive; } } }