private Func <WeakReference <ConcurrentAsyncEnumerator <T> >, Task> BuildProducerWrapperFactory( Func <ConcurrentAsyncProducer <T>, Task> producerFunc ) { return(async weakEnumerator => { ConcurrentAsyncEnumerator <T> enumerator; weakEnumerator.TryGetTarget(out enumerator); Trace2("Producer container started"); var producer = new ConcurrentAsyncProducer <T>(item => enumerator.Yield(item), _cancellationToken); try { // Wait for the consumer to call MoveNext() the first time. // This is only necessary to synchronize the enumerator when the // producer is started. await enumerator._emitNextValue; Trace2("Invoker user produder factory"); await producerFunc(producer); enumerator.EndOfStream(); } catch (OperationCanceledException) { // The consumer decided to stop the enumeration (by calling Dispose or DisposeAsync). // The idea here is to make sure that any code after the call to Yield that caused // this exception is not executed. // Throwing an exception is the only way to ensure that resources are cleaned up // correctly (try/catch/finally). // // In the the case where // * The consumer cancel the enumeration by calling Dispose (or DisposeAsync) // * The consumer invoke Yield again // * Yield will then throw an ObjectDisposedException to signal // that something is not working as intended. // // But there are cases that are not easily handled. // If the producer code has a "catch all" clause (catch {} or catch(Exception){}) // then the producer need to explicitely handle cooperatively to stop the execution // correctly. enumerator.EndOfStream(); } catch (Exception e) { // There was an exception (other than EnumerationAbandonedException) // in the producer code. enumerator.EndOfStream(e); // Ensure that this is propagated in Dispose too throw; } }); }
internal ConcurrentAsyncEnumerator(Func <ConcurrentAsyncProducer <T>, Task> producerFunc) { var producer = new ConcurrentAsyncProducer <T>(async item => { await valueBufferBlock.SendAsync(item).ConfigureAwait(false); }); producerFunc(producer).ContinueWith(t => { if (t.Exception != null) { exception = t.Exception; } valueBufferBlock.Complete(); }, TaskContinuationOptions.ExecuteSynchronously); }
internal ConcurrentAsyncEnumerator(Func <ConcurrentAsyncProducer <T>, Task> producerFunc) { _producer = async enumerator => { var producer = new ConcurrentAsyncProducer <T>(item => enumerator.Yield(item)); try { // Wait for the consumer to call MoveNext() the first time. // Note that after the first time, this will be always signalled. // This is only necessary to synchronize the enumerator when the // producer is started. await _moveNextSignal.Wait().ConfigureAwait(false); await producerFunc(producer); } finally { enumerator.EndOfStream(); } }; }
internal ConversionObserver(ConcurrentAsyncProducer <T> producer) { this.producer = producer; }
public static Task ForEach <T>(this IAsyncEnumerable <T> enumerable, ConcurrentAsyncProducer <T> producer) { return(ForEach(enumerable, loopState => producer.Yield(loopState.Item))); }