|
- using System;
- using System.Threading;
-
- namespace Cysharp.Threading.Tasks.Linq
- {
- public static partial class UniTaskAsyncEnumerable
- {
- public static IUniTaskAsyncEnumerable<TSource> Queue<TSource>(this IUniTaskAsyncEnumerable<TSource> source)
- {
- return new QueueOperator<TSource>(source);
- }
- }
-
- internal sealed class QueueOperator<TSource> : IUniTaskAsyncEnumerable<TSource>
- {
- readonly IUniTaskAsyncEnumerable<TSource> source;
-
- public QueueOperator(IUniTaskAsyncEnumerable<TSource> source)
- {
- this.source = source;
- }
-
- public IUniTaskAsyncEnumerator<TSource> GetAsyncEnumerator(CancellationToken cancellationToken = default)
- {
- return new _Queue(source, cancellationToken);
- }
-
- sealed class _Queue : IUniTaskAsyncEnumerator<TSource>
- {
- readonly IUniTaskAsyncEnumerable<TSource> source;
- CancellationToken cancellationToken;
-
- Channel<TSource> channel;
- IUniTaskAsyncEnumerator<TSource> channelEnumerator;
- IUniTaskAsyncEnumerator<TSource> sourceEnumerator;
- bool channelClosed;
-
- public _Queue(IUniTaskAsyncEnumerable<TSource> source, CancellationToken cancellationToken)
- {
- this.source = source;
- this.cancellationToken = cancellationToken;
- }
-
- public TSource Current => channelEnumerator.Current;
-
- public UniTask<bool> MoveNextAsync()
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (sourceEnumerator == null)
- {
- sourceEnumerator = source.GetAsyncEnumerator(cancellationToken);
- channel = Channel.CreateSingleConsumerUnbounded<TSource>();
-
- channelEnumerator = channel.Reader.ReadAllAsync().GetAsyncEnumerator(cancellationToken);
-
- ConsumeAll(this, sourceEnumerator, channel).Forget();
- }
-
- return channelEnumerator.MoveNextAsync();
- }
-
- static async UniTaskVoid ConsumeAll(_Queue self, IUniTaskAsyncEnumerator<TSource> enumerator, ChannelWriter<TSource> 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());
- }
- }
- }
- }
- }
|