Browse Source

Removed locks form Ref<T> (#19476)

pull/19488/head
Nikita Tsukanov 6 months ago
committed by GitHub
parent
commit
86609c100c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 92
      src/Avalonia.Base/Utilities/Ref.cs

92
src/Avalonia.Base/Utilities/Ref.cs

@ -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) + ">");
} }
} }

Loading…
Cancel
Save