// 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.Linq
{
public static partial class AsyncEnumerableEx
{
///
/// Returns a non-terminating async-enumerable sequence, which can be used to denote an infinite duration (e.g. when using reactive joins).
///
/// The type used for the type parameter of the resulting sequence.
/// An async-enumerable sequence whose consumers will never resume after awaiting .
public static IAsyncEnumerable Never() => NeverAsyncEnumerable.Instance;
private sealed class NeverAsyncEnumerable : IAsyncEnumerable
{
internal static readonly NeverAsyncEnumerable Instance = new NeverAsyncEnumerable();
public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.
return new NeverAsyncEnumerator(cancellationToken);
}
private sealed class NeverAsyncEnumerator : IAsyncEnumerator
{
private readonly CancellationToken _token;
private CancellationTokenRegistration _registration;
private bool _once;
public NeverAsyncEnumerator(CancellationToken token) => _token = token;
public TValue Current => throw new InvalidOperationException();
public ValueTask DisposeAsync()
{
_registration.Dispose();
return default;
}
public ValueTask MoveNextAsync()
{
if (_once)
{
return new ValueTask(false);
}
_once = true;
var task = new TaskCompletionSource();
_registration = _token.Register(state => ((TaskCompletionSource)state).TrySetCanceled(_token), task);
return new ValueTask(task.Task);
}
}
}
}
}