// 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;
}
}
}