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
2424using System ;
2525using 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