// 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; using System.Threading.Tasks; namespace System.Reactive.Disposables { public abstract class StableCompositeAsyncDisposable : IAsyncDisposable { public static StableCompositeAsyncDisposable Create(IAsyncDisposable disposable1, IAsyncDisposable disposable2) { if (disposable1 == null) throw new ArgumentNullException(nameof(disposable1)); if (disposable2 == null) throw new ArgumentNullException(nameof(disposable2)); return new Binary(disposable1, disposable2); } public static StableCompositeAsyncDisposable Create(params IAsyncDisposable[] disposables) { if (disposables == null) throw new ArgumentNullException(nameof(disposables)); return new NAry(disposables); } public static StableCompositeAsyncDisposable Create(IEnumerable disposables) { if (disposables == null) throw new ArgumentNullException(nameof(disposables)); return new NAry(disposables); } public abstract ValueTask DisposeAsync(); private sealed class Binary : StableCompositeAsyncDisposable { private volatile IAsyncDisposable _disposable1; private volatile IAsyncDisposable _disposable2; public Binary(IAsyncDisposable disposable1, IAsyncDisposable disposable2) { _disposable1 = disposable1; _disposable2 = disposable2; } public override async ValueTask DisposeAsync() { var d1 = Interlocked.Exchange(ref _disposable1, null); if (d1 != null) { await d1.DisposeAsync().ConfigureAwait(false); } var d2 = Interlocked.Exchange(ref _disposable2, null); if (d2 != null) { await d2.DisposeAsync().ConfigureAwait(false); } } } private sealed class NAry : StableCompositeAsyncDisposable { private volatile List _disposables; public NAry(IAsyncDisposable[] disposables) : this((IEnumerable)disposables) { } public NAry(IEnumerable disposables) { _disposables = new List(disposables); } public override async ValueTask DisposeAsync() { var old = Interlocked.Exchange(ref _disposables, null); if (old != null) { foreach (var d in old) { if (d != null) { await d.DisposeAsync().ConfigureAwait(false); } } } } } } }