|
@@ -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>
|
|
@@ -27,5 +29,111 @@ namespace System.Reactive.Disposables
|
|
|
|
|
|
return new AnonymousDisposable(dispose);
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
|
|
|
+ /// </summary>
|
|
|
+ internal static IDisposable GetValue(ref IDisposable fieldRef)
|
|
|
+ {
|
|
|
+ var current = Volatile.Read(ref fieldRef);
|
|
|
+
|
|
|
+ return current == BooleanDisposable.True
|
|
|
+ ? null
|
|
|
+ : current;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
|
|
|
+ /// </summary>
|
|
|
+ internal static IDisposable GetValueOrDefault(ref IDisposable fieldRef)
|
|
|
+ {
|
|
|
+ var current = Volatile.Read(ref fieldRef);
|
|
|
+
|
|
|
+ return current == BooleanDisposable.True
|
|
|
+ ? DefaultDisposable.Instance
|
|
|
+ : current;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static bool TrySetSingle(ref IDisposable fieldRef, IDisposable value)
|
|
|
+ {
|
|
|
+ var old = Interlocked.CompareExchange(ref fieldRef, value, null);
|
|
|
+ if (old == null)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (old != BooleanDisposable.True)
|
|
|
+ throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
|
|
|
+
|
|
|
+ value?.Dispose();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static bool TrySetMultiple(ref IDisposable fieldRef, IDisposable value)
|
|
|
+ {
|
|
|
+ // Let's read the current value atomically (also prevents reordering).
|
|
|
+ var old = Volatile.Read(ref fieldRef);
|
|
|
+
|
|
|
+ for (; ; )
|
|
|
+ {
|
|
|
+ // If it is the disposed instance, dispose the value.
|
|
|
+ if (old == BooleanDisposable.True)
|
|
|
+ {
|
|
|
+ value?.Dispose();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Atomically swap in the new value and get back the old.
|
|
|
+ var b = Interlocked.CompareExchange(ref fieldRef, value, old);
|
|
|
+
|
|
|
+ // If the old and new are the same, the swap was successful and we can quit
|
|
|
+ if (old == b)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Otherwise, make the old reference the current and retry.
|
|
|
+ old = b;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static bool TrySetSerial(ref IDisposable fieldRef, IDisposable value)
|
|
|
+ {
|
|
|
+ var copy = Volatile.Read(ref fieldRef);
|
|
|
+ for (; ; )
|
|
|
+ {
|
|
|
+ if (copy == BooleanDisposable.True)
|
|
|
+ {
|
|
|
+ value?.Dispose();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var current = Interlocked.CompareExchange(ref fieldRef, value, copy);
|
|
|
+ if (current == copy)
|
|
|
+ {
|
|
|
+ copy?.Dispose();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ copy = current;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static bool GetIsDisposed(ref IDisposable fieldRef)
|
|
|
+ {
|
|
|
+ // 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 fieldRef) == BooleanDisposable.True;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal static bool TryDispose(ref IDisposable fieldRef)
|
|
|
+ {
|
|
|
+ var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
|
|
|
+
|
|
|
+ if (old == BooleanDisposable.True)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ old?.Dispose();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
}
|