Skip to content

Commit a756f10

Browse files
committed
Avalonia: Removed locks form Ref<T> (AvaloniaUI/Avalonia#19476)
1 parent 1c94bb5 commit a756f10

File tree

1 file changed

+47
-46
lines changed

1 file changed

+47
-46
lines changed

Caly.Core/Utilities/Ref.cs

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*/
2020

2121
// Code from Avalonia
22-
// https://github.com/AvaloniaUI/Avalonia/blob/b15ee692da32dfa349c462c4c491a9cf652325b3/src/Avalonia.Base/Utilities/Ref.cs
22+
// https://github.com/AvaloniaUI/Avalonia/blob/86609c100c18639454076161c200744631b44c6a/src/Avalonia.Base/Utilities/Ref.cs
2323

2424
using System;
2525
using System.Runtime.ConstrainedExecution;
@@ -51,6 +51,7 @@ public interface IRef<out T> : IDisposable where T : class
5151
/// <returns>A reference to the value as the new type but sharing the refcount.</returns>
5252
IRef<TResult> CloneAs<TResult>() where TResult : class;
5353

54+
5455
/// <summary>
5556
/// The current refcount of the object tracked in this reference. For debugging/unit test use only.
5657
/// </summary>
@@ -77,18 +78,18 @@ private sealed class RefCounter
7778

7879
public RefCounter(IDisposable item)
7980
{
80-
_item = item;
81+
_item = item ?? throw new ArgumentNullException();
8182
_refs = 1;
8283
}
8384

84-
public void AddRef()
85+
internal bool TryAddRef()
8586
{
8687
var old = _refs;
8788
while (true)
8889
{
8990
if (old == 0)
9091
{
91-
throw new ObjectDisposedException("Cannot add a reference to a nonreferenced item");
92+
return false;
9293
}
9394
var current = Interlocked.CompareExchange(ref _refs, old + 1, old);
9495
if (current == old)
@@ -97,6 +98,8 @@ public void AddRef()
9798
}
9899
old = current;
99100
}
101+
102+
return true;
100103
}
101104

102105
public void Release()
@@ -124,76 +127,74 @@ public void Release()
124127

125128
private sealed class Ref<T> : CriticalFinalizerObject, IRef<T> where T : class
126129
{
127-
private T? _item;
128-
private readonly RefCounter _counter;
129-
private readonly object _lock = new object();
130+
private volatile T? _item;
131+
private volatile RefCounter? _counter;
130132

131133
public Ref(T item, RefCounter counter)
132134
{
133135
_item = item;
134136
_counter = counter;
135137
}
136138

137-
public void Dispose()
139+
public void Dispose() => Dispose(true);
140+
void Dispose(bool disposing)
138141
{
139-
lock (_lock)
142+
var item = Interlocked.Exchange(ref _item, null);
143+
144+
if (item != null)
140145
{
141-
if (_item != null)
142-
{
143-
_counter.Release();
144-
_item = null;
145-
}
146-
GC.SuppressFinalize(this);
146+
var counter = _counter!;
147+
_counter = null;
148+
if (disposing)
149+
GC.SuppressFinalize(this);
150+
counter.Release();
147151
}
148152
}
149153

150154
~Ref()
151155
{
152-
Dispose();
156+
Dispose(false);
153157
System.Diagnostics.Debug.Assert(Item is null || RefCount == 0);
154158
}
155159

156-
public T Item
157-
{
158-
get
159-
{
160-
lock (_lock)
161-
{
162-
return _item!;
163-
}
164-
}
165-
}
160+
161+
public T Item => _item ?? throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
166162

167163
public IRef<T> Clone()
168164
{
169-
lock (_lock)
170-
{
171-
if (_item != null)
172-
{
173-
var newRef = new Ref<T>(_item, _counter);
174-
_counter.AddRef();
175-
return newRef;
176-
}
165+
// Snapshot current ref state so we don't care if it's disposed in the meantime.
166+
var counter = _counter;
167+
var item = _item;
168+
169+
// Check if ref was invalid
170+
if (item == null || counter == null)
177171
throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
178-
}
172+
173+
// Try to add a reference to the counter, if it fails, the item is disposed.
174+
if (!counter.TryAddRef())
175+
throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
176+
177+
return new Ref<T>(item, counter);
179178
}
180179

181180
public IRef<TResult> CloneAs<TResult>() where TResult : class
182181
{
183-
lock (_lock)
184-
{
185-
if (_item != null)
186-
{
187-
var castRef = new Ref<TResult>((TResult)(object)_item, _counter);
188-
Interlocked.MemoryBarrier();
189-
_counter.AddRef();
190-
return castRef;
191-
}
182+
// Snapshot current ref state so we don't care if it's disposed in the meantime.
183+
var counter = _counter;
184+
var item = (TResult?)(object?)_item;
185+
186+
// Check if ref was invalid
187+
if (item == null || counter == null)
192188
throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
193-
}
189+
190+
// Try to add a reference to the counter, if it fails, the item is disposed.
191+
if (!counter.TryAddRef())
192+
throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
193+
194+
return new Ref<TResult>(item, counter);
194195
}
195196

196-
public int RefCount => _counter.RefCount;
197+
public int RefCount => _counter?.RefCount ?? throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
197198
}
198199
}
199200
}

0 commit comments

Comments
 (0)