| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 | // Licensed to the .NET Foundation under one or more agreements.// 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.Collections.Generic;using System.Threading;using System.Threading.Tasks;namespace System.Linq{    // REVIEW: The base class below was introduced to avoid the overhead of storing a field of type TSource if the    //         value of the iterator can trivially be inferred from another field (e.g. in Repeat). It is also used    //         by the Defer operator in System.Interactive.Async. For some operators such as Where, Skip, Take, and    //         Concat, it could be used to retrieve the value from the underlying enumerator. However, performance    //         of this approach is a bit worse in some cases, so we don't go ahead with it for now. One decision to    //         make is whether it's okay for Current to throw an exception when MoveNextAsync returns false, e.g.    //         by omitting a null check for an enumerator field.    internal abstract partial class AsyncIteratorBase<TSource> : IAsyncEnumerable<TSource>, IAsyncEnumerator<TSource>    {        private readonly int _threadId;        protected AsyncIteratorState _state = AsyncIteratorState.New;        protected CancellationToken _cancellationToken;        protected AsyncIteratorBase()        {            _threadId = Environment.CurrentManagedThreadId;        }        public IAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken)        {            cancellationToken.ThrowIfCancellationRequested(); // NB: [LDM-2018-11-28] Equivalent to async iterator behavior.            var enumerator = _state == AsyncIteratorState.New && _threadId == Environment.CurrentManagedThreadId                ? this                : Clone();            enumerator._state = AsyncIteratorState.Allocated;            enumerator._cancellationToken = cancellationToken;            // REVIEW: If the final interface contains a CancellationToken here, should we check for a cancellation request            //         either here or in the first call to MoveNextAsync?            return enumerator;        }        public virtual ValueTask DisposeAsync()        {            _state = AsyncIteratorState.Disposed;            return default;        }        public abstract TSource Current { get; }        public async ValueTask<bool> MoveNextAsync()        {            // Note: MoveNext *must* be implemented as an async method to ensure            // that any exceptions thrown from the MoveNextCore call are handled             // by the try/catch, whether they're sync or async            if (_state == AsyncIteratorState.Disposed)            {                return false;            }            try            {                return await MoveNextCore().ConfigureAwait(false);            }            catch            {                await DisposeAsync().ConfigureAwait(false);                throw;            }        }        public abstract AsyncIteratorBase<TSource> Clone();        protected abstract ValueTask<bool> MoveNextCore();    }    internal abstract class AsyncIterator<TSource> : AsyncIteratorBase<TSource>    {        protected TSource _current;        public override TSource Current => _current;        public override ValueTask DisposeAsync()        {            _current = default;            return base.DisposeAsync();        }    }    internal enum AsyncIteratorState    {        New = 0,        Allocated = 1,        Iterating = 2,        Disposed = -1,    }}
 |