|
|
@@ -2,6 +2,8 @@
|
|
|
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
|
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
|
|
+using System.Threading;
|
|
|
+
|
|
|
namespace System.Reactive.Disposables
|
|
|
{
|
|
|
/// <summary>
|
|
|
@@ -9,7 +11,6 @@ namespace System.Reactive.Disposables
|
|
|
/// </summary>
|
|
|
public sealed class MultipleAssignmentDisposable : ICancelable
|
|
|
{
|
|
|
- private readonly object _gate = new object();
|
|
|
private IDisposable _current;
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -26,13 +27,10 @@ namespace System.Reactive.Disposables
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- lock (_gate)
|
|
|
- {
|
|
|
- // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
|
|
|
- // to the outside world (see the Disposable property getter), so no-one can ever assign
|
|
|
- // this value to us manually.
|
|
|
- return _current == BooleanDisposable.True;
|
|
|
- }
|
|
|
+ // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
|
|
|
+ // to the outside world (see the Disposable property getter), so no-one can ever assign
|
|
|
+ // this value to us manually.
|
|
|
+ return Volatile.Read(ref _current) == BooleanDisposable.True;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -44,28 +42,37 @@ namespace System.Reactive.Disposables
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- lock (_gate)
|
|
|
+ var a = Volatile.Read(ref _current);
|
|
|
+ // Don't leak the DISPOSED sentinel
|
|
|
+ if (a == BooleanDisposable.True)
|
|
|
{
|
|
|
- if (_current == BooleanDisposable.True /* see IsDisposed */)
|
|
|
- return DefaultDisposable.Instance; // Don't leak the sentinel value.
|
|
|
-
|
|
|
- return _current;
|
|
|
+ a = DefaultDisposable.Instance;
|
|
|
}
|
|
|
+ return a;
|
|
|
}
|
|
|
|
|
|
set
|
|
|
{
|
|
|
- var shouldDispose = false;
|
|
|
- lock (_gate)
|
|
|
+ // Let's read the current value atomically (also prevents reordering).
|
|
|
+ var old = Volatile.Read(ref _current);
|
|
|
+ for (;;)
|
|
|
{
|
|
|
- shouldDispose = (_current == BooleanDisposable.True /* see IsDisposed */);
|
|
|
- if (!shouldDispose)
|
|
|
+ // If it is the disposed instance, dispose the value.
|
|
|
+ if (old == BooleanDisposable.True)
|
|
|
{
|
|
|
- _current = value;
|
|
|
+ value?.Dispose();
|
|
|
+ return;
|
|
|
}
|
|
|
+ // Atomically swap in the new value and get back the old.
|
|
|
+ var b = Interlocked.CompareExchange(ref _current, value, old);
|
|
|
+ // If the old and new are the same, the swap was successful and we can quit
|
|
|
+ if (old == b)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // Otherwise, make the old reference the current and retry.
|
|
|
+ old = b;
|
|
|
}
|
|
|
- if (shouldDispose && value != null)
|
|
|
- value.Dispose();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -74,19 +81,20 @@ namespace System.Reactive.Disposables
|
|
|
/// </summary>
|
|
|
public void Dispose()
|
|
|
{
|
|
|
- var old = default(IDisposable);
|
|
|
-
|
|
|
- lock (_gate)
|
|
|
+ // Read the current atomically.
|
|
|
+ var a = Volatile.Read(ref _current);
|
|
|
+ // If it is the disposed instance, don't bother further.
|
|
|
+ if (a != BooleanDisposable.True)
|
|
|
{
|
|
|
- if (_current != BooleanDisposable.True /* see IsDisposed */)
|
|
|
+ // Atomically swap in the disposed instance.
|
|
|
+ a = Interlocked.Exchange(ref _current, BooleanDisposable.True);
|
|
|
+ // It is possible there was a concurrent Dispose call so don't need to call Dispose()
|
|
|
+ // on DISPOSED
|
|
|
+ if (a != BooleanDisposable.True)
|
|
|
{
|
|
|
- old = _current;
|
|
|
- _current = BooleanDisposable.True /* see IsDisposed */;
|
|
|
+ a?.Dispose();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if (old != null)
|
|
|
- old.Dispose();
|
|
|
}
|
|
|
}
|
|
|
}
|