// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Buffers; using System.Threading; namespace EpicGames.Core { /// /// Disposable reference to an underlying resource. The underlying resource is disposed once the last reference to it is disposed. /// public interface IRefCountedHandle : IDisposable { /// /// Increment the reference count and returns a new handle to it /// IRefCountedHandle AddRef(); } /// /// Disposable reference to an underlying resource. /// /// Type of the target resource public interface IRefCountedHandle : IRefCountedHandle { /// /// Accessor for the current ref count, for debugging purposes. /// int RefCount { get; } /// /// Target of the reference /// T Target { get; } /// new IRefCountedHandle AddRef(); /// IRefCountedHandle IRefCountedHandle.AddRef() => AddRef(); } /// /// Utility methods for creating ref counted handles /// public static class RefCountedHandle { /// /// Creates a reference counted handle to a disposable resource. /// public static IRefCountedHandle Create(T target) where T : IDisposable { return new RefCountedHandle(target, target); } /// /// Creates a reference counted handle to a resource with an explicit disposer. /// public static IRefCountedHandle Create(T target, IDisposable? owner) { return new RefCountedHandle(target, owner); } /// /// Creates a reference counted handle to a memory owner. /// public static IRefCountedHandle> Create(IMemoryOwner target) where T : struct { return new RefCountedHandle>(target.Memory, target); } } /// /// Concrete implementation of /// public sealed class RefCountedHandle : IRefCountedHandle { readonly RefCountedDisposer? _refCountedDisposer; T _target; /// public int RefCount => _refCountedDisposer?.RefCount ?? throw new ObjectDisposedException(typeof(RefCountedHandle).Name); /// public T Target => _target ?? throw new ObjectDisposedException(typeof(RefCountedHandle).Name); /// /// Constructor /// /// Target value /// Owner of the target resource public RefCountedHandle(T target, IDisposable? owner) { _refCountedDisposer = new RefCountedDisposer(owner); _target = target; } private RefCountedHandle(T value, RefCountedDisposer disposer) { _refCountedDisposer = disposer; _target = value; } /// public void Dispose() { if (_refCountedDisposer != null) { _refCountedDisposer.Release(); _target = default!; } } /// public IRefCountedHandle AddRef() { ObjectDisposedException.ThrowIf(_refCountedDisposer == null, typeof(RefCountedHandle).Name); _refCountedDisposer.AddRef(); return new RefCountedHandle(Target, _refCountedDisposer); } } /// /// Tracks a ref count value and disposes of a resource once the reference count is zero /// public sealed class RefCountedDisposer { IDisposable? _owner; int _value = 1; /// /// Current reference count /// public int RefCount => _value; /// /// Constructor /// public RefCountedDisposer(IDisposable? owner, int initialValue = 1) { _owner = owner; _value = initialValue; } /// /// Increment the current value /// /// The incremented value public int AddRef() => Interlocked.Increment(ref _value); /// /// Decrement the current value /// /// The decremented value public int Release() { int count = Interlocked.Decrement(ref _value); if (count == 0) { _owner?.Dispose(); _owner = null; } return count; } } }