committed by
GitHub
29 changed files with 589 additions and 143 deletions
@ -0,0 +1,209 @@ |
|||
using System; |
|||
using System.Runtime.ConstrainedExecution; |
|||
using System.Threading; |
|||
|
|||
namespace Avalonia.Utilities |
|||
{ |
|||
/// <summary>
|
|||
/// A ref-counted wrapper for a disposable object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T"></typeparam>
|
|||
public interface IRef<out T> : IDisposable where T : class |
|||
{ |
|||
/// <summary>
|
|||
/// The item that is being ref-counted.
|
|||
/// </summary>
|
|||
T Item { get; } |
|||
|
|||
/// <summary>
|
|||
/// Create another reference to this object and increment the refcount.
|
|||
/// </summary>
|
|||
/// <returns>A new reference to this object.</returns>
|
|||
IRef<T> Clone(); |
|||
|
|||
/// <summary>
|
|||
/// Create another reference to the same object, but cast the object to a different type.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">The type of the new reference.</typeparam>
|
|||
/// <returns>A reference to the value as the new type but sharing the refcount.</returns>
|
|||
IRef<TResult> CloneAs<TResult>() where TResult : class; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// The current refcount of the object tracked in this reference. For debugging/unit test use only.
|
|||
/// </summary>
|
|||
int RefCount { get; } |
|||
} |
|||
|
|||
|
|||
|
|||
public static class RefCountable |
|||
{ |
|||
/// <summary>
|
|||
/// Create a reference counted object wrapping the given item.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of item.</typeparam>
|
|||
/// <param name="item">The item to refcount.</param>
|
|||
/// <returns>The refcounted reference to the item.</returns>
|
|||
public static IRef<T> Create<T>(T item) where T : class, IDisposable |
|||
{ |
|||
return new Ref<T>(item, new RefCounter(item)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Create an non-owning non-clonable reference to an item.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of item.</typeparam>
|
|||
/// <param name="item">The item.</param>
|
|||
/// <returns>A temporary reference that cannot be cloned that doesn't own the element.</returns>
|
|||
public static IRef<T> CreateUnownedNotClonable<T>(T item) where T : class |
|||
=> new TempRef<T>(item); |
|||
|
|||
class TempRef<T> : IRef<T> where T : class |
|||
{ |
|||
public void Dispose() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public TempRef(T item) |
|||
{ |
|||
Item = item; |
|||
} |
|||
|
|||
public T Item { get; } |
|||
public IRef<T> Clone() => throw new NotSupportedException(); |
|||
|
|||
public IRef<TResult> CloneAs<TResult>() where TResult : class |
|||
=> throw new NotSupportedException(); |
|||
|
|||
public int RefCount => 1; |
|||
} |
|||
|
|||
class RefCounter |
|||
{ |
|||
private IDisposable _item; |
|||
private volatile int _refs; |
|||
|
|||
public RefCounter(IDisposable item) |
|||
{ |
|||
_item = item; |
|||
_refs = 1; |
|||
} |
|||
|
|||
public void AddRef() |
|||
{ |
|||
var old = _refs; |
|||
while (true) |
|||
{ |
|||
if (old == 0) |
|||
{ |
|||
throw new ObjectDisposedException("Cannot add a reference to a nonreferenced item"); |
|||
} |
|||
var current = Interlocked.CompareExchange(ref _refs, old + 1, old); |
|||
if (current == old) |
|||
{ |
|||
break; |
|||
} |
|||
old = current; |
|||
} |
|||
} |
|||
|
|||
public void Release() |
|||
{ |
|||
var old = _refs; |
|||
while (true) |
|||
{ |
|||
var current = Interlocked.CompareExchange(ref _refs, old - 1, old); |
|||
|
|||
if (current == old) |
|||
{ |
|||
if (old == 1) |
|||
{ |
|||
_item.Dispose(); |
|||
_item = null; |
|||
} |
|||
break; |
|||
} |
|||
old = current; |
|||
} |
|||
} |
|||
|
|||
internal int RefCount => _refs; |
|||
} |
|||
|
|||
class Ref<T> : CriticalFinalizerObject, IRef<T> where T : class |
|||
{ |
|||
private T _item; |
|||
private RefCounter _counter; |
|||
private object _lock = new object(); |
|||
|
|||
public Ref(T item, RefCounter counter) |
|||
{ |
|||
_item = item; |
|||
_counter = counter; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (_item != null) |
|||
{ |
|||
_counter.Release(); |
|||
_item = null; |
|||
} |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
} |
|||
|
|||
~Ref() |
|||
{ |
|||
_counter?.Release(); |
|||
} |
|||
|
|||
public T Item |
|||
{ |
|||
get |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
return _item; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public IRef<T> Clone() |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (_item != null) |
|||
{ |
|||
var newRef = new Ref<T>(_item, _counter); |
|||
_counter.AddRef(); |
|||
return newRef; |
|||
} |
|||
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|||
} |
|||
} |
|||
|
|||
public IRef<TResult> CloneAs<TResult>() where TResult : class |
|||
{ |
|||
lock (_lock) |
|||
{ |
|||
if (_item != null) |
|||
{ |
|||
var castRef = new Ref<TResult>((TResult)(object)_item, _counter); |
|||
Interlocked.MemoryBarrier(); |
|||
_counter.AddRef(); |
|||
return castRef; |
|||
} |
|||
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|||
} |
|||
} |
|||
|
|||
public int RefCount => _counter.RefCount; |
|||
} |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue