Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.Core/RefCountedHandle.cs
2025-05-18 13:04:45 +08:00

168 lines
4.2 KiB
C#

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