// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace System.Reactive.Disposables
{
internal enum TrySetSingleResult
{
Success,
AlreadyAssigned,
Disposed
}
public static partial class Disposable
{
///
/// Gets the value stored in or a null if
/// was already disposed.
///
internal static IDisposable? GetValue([NotNullIfNotNull(nameof(fieldRef))] /*in*/ ref IDisposable? fieldRef)
{
var current = Volatile.Read(ref fieldRef);
return current == BooleanDisposable.True
? null
: current;
}
///
/// Gets the value stored in or a no-op-Disposable if
/// was already disposed.
///
[return: NotNullIfNotNull(nameof(fieldRef))]
internal static IDisposable? GetValueOrDefault([NotNullIfNotNull(nameof(fieldRef))] /*in*/ ref IDisposable? fieldRef)
{
var current = Volatile.Read(ref fieldRef);
return current == BooleanDisposable.True
? EmptyDisposable.Instance
: current;
}
///
/// Tries to assign to .
///
/// A value indicating the outcome of the operation.
internal static TrySetSingleResult TrySetSingle([NotNullIfNotNull(nameof(value))] ref IDisposable? fieldRef, IDisposable? value)
{
var old = Interlocked.CompareExchange(ref fieldRef, value, null);
if (old == null)
{
return TrySetSingleResult.Success;
}
if (old != BooleanDisposable.True)
{
return TrySetSingleResult.AlreadyAssigned;
}
value?.Dispose();
return TrySetSingleResult.Disposed;
}
///
/// Tries to assign to . If
/// is not disposed and is assigned a different value, it will not be disposed.
///
/// true if was successfully assigned to .
/// false has been disposed.
internal static bool TrySetMultiple([NotNullIfNotNull(nameof(value))] 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;
}
}
///
/// Tries to assign to . If
/// is not disposed and is assigned a different value, it will be disposed.
///
/// true if was successfully assigned to .
/// false has been disposed.
internal static bool TrySetSerial([NotNullIfNotNull(nameof(value))] 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;
}
}
///
/// Disposes .
///
internal static void Dispose([NotNullIfNotNull(nameof(fieldRef))] ref IDisposable? fieldRef)
{
var old = Interlocked.Exchange(ref fieldRef, BooleanDisposable.True);
if (old != BooleanDisposable.True)
{
old?.Dispose();
}
}
}
}