using System; using System.Threading; namespace Cysharp.Threading.Tasks.Linq { public static partial class UniTaskAsyncEnumerable { public static IUniTaskAsyncEnumerable Queue(this IUniTaskAsyncEnumerable source) { return new QueueOperator(source); } } internal sealed class QueueOperator : IUniTaskAsyncEnumerable { readonly IUniTaskAsyncEnumerable source; public QueueOperator(IUniTaskAsyncEnumerable source) { this.source = source; } public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) { return new _Queue(source, cancellationToken); } sealed class _Queue : IUniTaskAsyncEnumerator { readonly IUniTaskAsyncEnumerable source; CancellationToken cancellationToken; Channel channel; IUniTaskAsyncEnumerator channelEnumerator; IUniTaskAsyncEnumerator sourceEnumerator; bool channelClosed; public _Queue(IUniTaskAsyncEnumerable source, CancellationToken cancellationToken) { this.source = source; this.cancellationToken = cancellationToken; } public TSource Current => channelEnumerator.Current; public UniTask MoveNextAsync() { cancellationToken.ThrowIfCancellationRequested(); if (sourceEnumerator == null) { sourceEnumerator = source.GetAsyncEnumerator(cancellationToken); channel = Channel.CreateSingleConsumerUnbounded(); channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken); ConsumeAll(this, sourceEnumerator, channel).Forget(); } return channelEnumerator.MoveNextAsync(); } static async UniTaskVoid ConsumeAll(_Queue self, IUniTaskAsyncEnumerator enumerator, ChannelWriter writer) { try { while (await enumerator.MoveNextAsync()) { writer.TryWrite(enumerator.Current); } writer.TryComplete(); } catch (Exception ex) { writer.TryComplete(ex); } finally { self.channelClosed = true; await enumerator.DisposeAsync(); } } public async UniTask DisposeAsync() { if (sourceEnumerator != null) { await sourceEnumerator.DisposeAsync(); } if (channelEnumerator != null) { await channelEnumerator.DisposeAsync(); } if (!channelClosed) { channelClosed = true; channel.Writer.TryComplete(new OperationCanceledException()); } } } } }