// 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.Collections.Generic;
using System.Threading;
namespace System.Reactive.Disposables
{
///
/// Represents a group of disposable resources that are disposed together.
///
public abstract class StableCompositeDisposable : ICancelable
{
///
/// Creates a new group containing two disposable resources that are disposed together.
///
/// The first disposable resource to add to the group.
/// The second disposable resource to add to the group.
/// Group of disposable resources that are disposed together.
public static ICancelable Create(IDisposable disposable1, IDisposable disposable2)
{
if (disposable1 == null)
{
throw new ArgumentNullException(nameof(disposable1));
}
if (disposable2 == null)
{
throw new ArgumentNullException(nameof(disposable2));
}
return new Binary(disposable1, disposable2);
}
///
/// Creates a new group of disposable resources that are disposed together.
///
/// Disposable resources to add to the group.
/// Group of disposable resources that are disposed together.
public static ICancelable Create(params IDisposable[] disposables)
{
if (disposables == null)
{
throw new ArgumentNullException(nameof(disposables));
}
return new NAryArray(disposables);
}
///
/// Creates a group of disposable resources that are disposed together
/// and without copying or checking for nulls inside the group.
///
/// The array of disposables that is trusted
/// to not contain nulls and gives no need to defensively copy it.
/// Group of disposable resources that are disposed together.
internal static ICancelable CreateTrusted(params IDisposable[] disposables)
{
return new NAryTrustedArray(disposables);
}
///
/// Creates a new group of disposable resources that are disposed together.
///
/// Disposable resources to add to the group.
/// Group of disposable resources that are disposed together.
public static ICancelable Create(IEnumerable disposables)
{
if (disposables == null)
{
throw new ArgumentNullException(nameof(disposables));
}
return new NAryEnumerable(disposables);
}
///
/// Disposes all disposables in the group.
///
public abstract void Dispose();
///
/// Gets a value that indicates whether the object is disposed.
///
public abstract bool IsDisposed
{
get;
}
private sealed class Binary : StableCompositeDisposable
{
private IDisposable? _disposable1;
private IDisposable? _disposable2;
public Binary(IDisposable disposable1, IDisposable disposable2)
{
Volatile.Write(ref _disposable1, disposable1);
Volatile.Write(ref _disposable2, disposable2);
}
public override bool IsDisposed => Disposable.GetIsDisposed(ref _disposable1);
public override void Dispose()
{
Disposable.Dispose(ref _disposable1);
Disposable.Dispose(ref _disposable2);
}
}
private sealed class NAryEnumerable : StableCompositeDisposable
{
private volatile List? _disposables;
public NAryEnumerable(IEnumerable disposables)
{
_disposables = new List(disposables);
//
// Doing this on the list to avoid duplicate enumeration of disposables.
//
if (_disposables.Contains(null!))
{
throw new ArgumentException(Strings_Core.DISPOSABLES_CANT_CONTAIN_NULL, nameof(disposables));
}
}
public override bool IsDisposed => _disposables == null;
public override void Dispose()
{
var old = Interlocked.Exchange(ref _disposables, null);
if (old != null)
{
foreach (var d in old)
{
d.Dispose();
}
}
}
}
private sealed class NAryArray : StableCompositeDisposable
{
private IDisposable[]? _disposables;
public NAryArray(IDisposable[] disposables)
{
if (Array.IndexOf(disposables, null!) != -1)
{
throw new ArgumentException(Strings_Core.DISPOSABLES_CANT_CONTAIN_NULL, nameof(disposables));
}
var n = disposables.Length;
var ds = new IDisposable[n];
Array.Copy(disposables, 0, ds, 0, n);
Volatile.Write(ref _disposables, ds);
}
public override bool IsDisposed => Volatile.Read(ref _disposables) == null;
public override void Dispose()
{
var old = Interlocked.Exchange(ref _disposables, null);
if (old != null)
{
foreach (var d in old)
{
d.Dispose();
}
}
}
}
///
/// A stable composite that doesn't do defensive copy of
/// the input disposable array nor checks it for null.
///
private sealed class NAryTrustedArray : StableCompositeDisposable
{
private IDisposable[]? _disposables;
public NAryTrustedArray(IDisposable[] disposables)
{
Volatile.Write(ref _disposables, disposables);
}
public override bool IsDisposed => Volatile.Read(ref _disposables) == null;
public override void Dispose()
{
var old = Interlocked.Exchange(ref _disposables, null);
if (old != null)
{
foreach (var d in old)
{
d.Dispose();
}
}
}
}
}
}