|
|
@ -57,18 +57,18 @@ namespace Avalonia.Utilities |
|
|
|
|
|
|
|
|
public RefCounter(IDisposable item) |
|
|
public RefCounter(IDisposable item) |
|
|
{ |
|
|
{ |
|
|
_item = item; |
|
|
_item = item ?? throw new ArgumentNullException(); |
|
|
_refs = 1; |
|
|
_refs = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void AddRef() |
|
|
internal bool TryAddRef() |
|
|
{ |
|
|
{ |
|
|
var old = _refs; |
|
|
var old = _refs; |
|
|
while (true) |
|
|
while (true) |
|
|
{ |
|
|
{ |
|
|
if (old == 0) |
|
|
if (old == 0) |
|
|
{ |
|
|
{ |
|
|
throw new ObjectDisposedException("Cannot add a reference to a nonreferenced item"); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
var current = Interlocked.CompareExchange(ref _refs, old + 1, old); |
|
|
var current = Interlocked.CompareExchange(ref _refs, old + 1, old); |
|
|
if (current == old) |
|
|
if (current == old) |
|
|
@ -77,6 +77,8 @@ namespace Avalonia.Utilities |
|
|
} |
|
|
} |
|
|
old = current; |
|
|
old = current; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void Release() |
|
|
public void Release() |
|
|
@ -101,12 +103,11 @@ namespace Avalonia.Utilities |
|
|
|
|
|
|
|
|
internal int RefCount => _refs; |
|
|
internal int RefCount => _refs; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class Ref<T> : CriticalFinalizerObject, IRef<T> where T : class |
|
|
class Ref<T> : CriticalFinalizerObject, IRef<T> where T : class |
|
|
{ |
|
|
{ |
|
|
private T? _item; |
|
|
private volatile T? _item; |
|
|
private readonly RefCounter _counter; |
|
|
private volatile RefCounter? _counter; |
|
|
private readonly object _lock = new object(); |
|
|
|
|
|
|
|
|
|
|
|
public Ref(T item, RefCounter counter) |
|
|
public Ref(T item, RefCounter counter) |
|
|
{ |
|
|
{ |
|
|
@ -114,65 +115,64 @@ namespace Avalonia.Utilities |
|
|
_counter = counter; |
|
|
_counter = counter; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void Dispose() |
|
|
public void Dispose() => Dispose(true); |
|
|
|
|
|
void Dispose(bool disposing) |
|
|
{ |
|
|
{ |
|
|
lock (_lock) |
|
|
var item = Interlocked.Exchange(ref _item, null); |
|
|
|
|
|
|
|
|
|
|
|
if (item != null) |
|
|
{ |
|
|
{ |
|
|
if (_item != null) |
|
|
var counter = _counter!; |
|
|
{ |
|
|
_counter = null; |
|
|
_counter.Release(); |
|
|
if (disposing) |
|
|
_item = null; |
|
|
GC.SuppressFinalize(this); |
|
|
} |
|
|
counter.Release(); |
|
|
GC.SuppressFinalize(this); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
~Ref() |
|
|
~Ref() |
|
|
{ |
|
|
{ |
|
|
Dispose(); |
|
|
Dispose(false); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public T Item |
|
|
public T Item => _item ?? throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
{ |
|
|
|
|
|
get |
|
|
|
|
|
{ |
|
|
|
|
|
lock (_lock) |
|
|
|
|
|
{ |
|
|
|
|
|
return _item!; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public IRef<T> Clone() |
|
|
public IRef<T> Clone() |
|
|
{ |
|
|
{ |
|
|
lock (_lock) |
|
|
// Snapshot current ref state so we don't care if it's disposed in the meantime.
|
|
|
{ |
|
|
var counter = _counter; |
|
|
if (_item != null) |
|
|
var item = _item; |
|
|
{ |
|
|
|
|
|
var newRef = new Ref<T>(_item, _counter); |
|
|
// Check if ref was invalid
|
|
|
_counter.AddRef(); |
|
|
if (item == null || counter == null) |
|
|
return newRef; |
|
|
|
|
|
} |
|
|
|
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
} |
|
|
|
|
|
|
|
|
// Try to add a reference to the counter, if it fails, the item is disposed.
|
|
|
|
|
|
if (!counter.TryAddRef()) |
|
|
|
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
|
|
|
|
|
|
|
|
|
return new Ref<T>(item, counter); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public IRef<TResult> CloneAs<TResult>() where TResult : class |
|
|
public IRef<TResult> CloneAs<TResult>() where TResult : class |
|
|
{ |
|
|
{ |
|
|
lock (_lock) |
|
|
// Snapshot current ref state so we don't care if it's disposed in the meantime.
|
|
|
{ |
|
|
var counter = _counter; |
|
|
if (_item != null) |
|
|
var item = (TResult?)(object?)_item; |
|
|
{ |
|
|
|
|
|
var castRef = new Ref<TResult>((TResult)(object)_item, _counter); |
|
|
// Check if ref was invalid
|
|
|
Interlocked.MemoryBarrier(); |
|
|
if (item == null || counter == null) |
|
|
_counter.AddRef(); |
|
|
|
|
|
return castRef; |
|
|
|
|
|
} |
|
|
|
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
} |
|
|
|
|
|
|
|
|
// Try to add a reference to the counter, if it fails, the item is disposed.
|
|
|
|
|
|
if (!counter.TryAddRef()) |
|
|
|
|
|
throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
|
|
|
|
|
|
|
|
|
return new Ref<TResult>(item, counter); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public int RefCount => _counter.RefCount; |
|
|
public int RefCount => _counter?.RefCount ?? throw new ObjectDisposedException("Ref<" + typeof(T) + ">"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|