示例#1
0
        /// <summary>
        /// Asynchronously writes all entries from the source to the channel.
        /// </summary>
        /// <typeparam name="T">The input type of the channel.</typeparam>
        /// <param name="target">The channel to write to.</param>
        /// <param name="source">The asynchronous source data to use.</param>
        /// <param name="complete">If true, will call .Complete() if all the results have successfully been written (or the source is emtpy).</param>
        /// <param name="cancellationToken">An optional cancellation token.</param>
        /// <returns>A task containing the count of items written that completes when all the data has been written to the channel writer.
        /// The count should be ignored if the number of iterations could exceed the max value of long.</returns>
        public static async ValueTask <long> WriteAllAsync <T>(this ChannelWriter <T> target,
                                                               IEnumerable <ValueTask <T> > source, bool complete = false, CancellationToken cancellationToken = default)
        {
            await target.WaitToWriteAndThrowIfClosedAsync(
                "The target channel was closed before writing could begin.",
                cancellationToken);

            long count = 0;
            var  next  = new ValueTask();

            foreach (var e in source)
            {
                var value = await e.ConfigureAwait(false);

                await next.ConfigureAwait(false);

                count++;
                next = target.WriteAsync(value, cancellationToken);
            }
            await next.ConfigureAwait(false);

            if (complete)
            {
                target.Complete();
            }

            return(count);
        }
        /// <summary>
        /// Asynchronously writes all entries from the source to the channel.
        /// </summary>
        /// <typeparam name="T">The input type of the channel.</typeparam>
        /// <param name="target">The channel to write to.</param>
        /// <param name="source">The asynchronous source data to use.</param>
        /// <param name="complete">If true, will call .Complete() if all the results have successfully been written (or the source is emtpy).</param>
        /// <param name="deferredExecution">If true, calls await Task.Yield() before writing.</param>
        /// <param name="cancellationToken">An optional cancellation token.</param>
        /// <returns>A task containing the count of items written that completes when all the data has been written to the channel writer.
        /// The count should be ignored if the number of iterations could exceed the max value of long.</returns>
        public static async ValueTask <long> WriteAllAsync <T>(this ChannelWriter <T> target,
                                                               IAsyncEnumerable <T> source, bool complete = false, bool deferredExecution = false, CancellationToken cancellationToken = default)
        {
            if (target is null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (source is null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            Contract.EndContractBlock();

            await target
            .WaitToWriteAndThrowIfClosedAsync(ChannelClosedMessage, deferredExecution, cancellationToken)
            .ConfigureAwait(false);

            try
            {
                long count = 0;
                var  next  = new ValueTask();
                await foreach (var value in source)
                {
                    await next.ConfigureAwait(false);

                    count++;
                    next = target.WriteAsync(value, cancellationToken);
                }
                await next.ConfigureAwait(false);

                return(count);
            }
            catch (Exception ex)
            {
                if (complete)
                {
                    target.Complete(ex);
                    complete = false;
                }
                throw;
            }
            finally
            {
                if (complete)
                {
                    target.Complete();
                }
            }
        }
示例#3
0
        /// <summary>
        /// Consumes all lines from a TextReader and writes them to a channel.
        /// </summary>
        /// <param name="source">The text reader to consume from.</param>
        /// <param name="complete">If true, will call .Complete() if all the results have successfully been written (or the source is emtpy).</param>
        /// <param name="cancellationToken">An optional cancellation token.</param>
        /// <returns>A task containing the count of items written that completes when all the data has been written to the channel writer.
        /// The count should be ignored if the number of iterations could exceed the max value of long.</returns>
        public static async ValueTask <long> WriteAllLines(this ChannelWriter <string> target,
                                                           TextReader source, bool complete = false, CancellationToken cancellationToken = default)
        {
            var next = target.WaitToWriteAndThrowIfClosedAsync(
                "The target channel was closed before writing could begin.",
                cancellationToken);

            await next.ConfigureAwait(false);

            long count = 0;
            var  more  = false;           // if it completed and actually returned false, no need to bubble the cancellation since it actually completed.

            while (!cancellationToken.IsCancellationRequested)
            {
                var line = await source.ReadLineAsync().ConfigureAwait(false);

                if (line == null)
                {
                    more = false;
                    break;
                }
                else
                {
                    more = true;
                }

                await next.ConfigureAwait(false);

                count++;
                next = target.WriteAsync(line, cancellationToken);
            }
            await next.ConfigureAwait(false);

            if (more)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }

            if (complete)
            {
                target.Complete();
            }

            return(count);
        }
示例#4
0
        /// <summary>
        /// Asynchronously writes all entries from the source to the channel.
        /// </summary>
        /// <typeparam name="T">The input type of the channel.</typeparam>
        /// <param name="target">The channel to write to.</param>
        /// <param name="maxConcurrency">The maximum number of concurrent operations.</param>
        /// <param name="source">The asynchronous source data to use.</param>
        /// <param name="complete">If true, will call .Complete() if all the results have successfully been written (or the source is emtpy).</param>
        /// <param name="cancellationToken">An optional cancellation token.</param>
        /// <returns>A task containing the count of items written that completes when all the data has been written to the channel writer.
        /// The count should be ignored if the number of iterations could exceed the max value of long.</returns>
        public static Task <long> WriteAllConcurrentlyAsync <T>(this ChannelWriter <T> target,
                                                                int maxConcurrency, IEnumerable <ValueTask <T> > source, bool complete = false, CancellationToken cancellationToken = default)
        {
            if (maxConcurrency < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(maxConcurrency), maxConcurrency, "Must be at least 1.");
            }
            Contract.EndContractBlock();

            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <long>(cancellationToken));
            }

            if (maxConcurrency == 1)
            {
                return(target.WriteAllAsync(source, complete, cancellationToken).AsTask());
            }

            var shouldWait = target
                             .WaitToWriteAndThrowIfClosedAsync("The target channel was closed before writing could begin.", cancellationToken)
                             .AsTask();    // ValueTasks can only have a single await.

            var enumerator = source.GetEnumerator();
            var writers    = new Task <long> [maxConcurrency];

            for (var w = 0; w < maxConcurrency; w++)
            {
                writers[w] = WriteAllAsyncCore();
            }

            return(Task
                   .WhenAll(writers)
                   .ContinueWith(t =>
            {
                if (complete)
                {
                    target.Complete();
                }

                if (t.IsFaulted)
                {
                    return Task.FromException <long>(t.Exception);
                }

                if (t.IsCanceled)
                {
                    return Task.FromCanceled <long>(cancellationToken);
                }

                return Task.FromResult(t.Result.Sum());
            }, TaskContinuationOptions.ExecuteSynchronously)
                   .Unwrap());

            // returns false if there's no more (wasn't cancelled).
            async Task <long> WriteAllAsyncCore()
            {
                await shouldWait;
                long  count = 0;
                var   next  = new ValueTask();
                var   potentiallyCancelled = true;               // if it completed and actually returned false, no need to bubble the cancellation since it actually completed.

                while (!cancellationToken.IsCancellationRequested &&
                       (potentiallyCancelled = TryMoveNextSynchronized(enumerator, out var e)))
                {
                    var value = await e.ConfigureAwait(false);

                    await next.ConfigureAwait(false);

                    count++;
                    next = target.TryWrite(value)                     // do this to avoid unneccesary early cancel.
                                                ? new ValueTask()
                                                : target.WriteAsync(value, cancellationToken);
                }
                await next.ConfigureAwait(false);

                if (potentiallyCancelled)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
                return(count);
            }
        }
        /// <summary>
        /// Consumes all lines from a TextReader and writes them to a channel.
        /// </summary>
        /// <param name="source">The text reader to consume from.</param>
        /// <param name="target">The channel to write to.</param>
        /// <param name="complete">If true, will call .Complete() if all the results have successfully been written (or the source is emtpy).</param>
        /// <param name="deferredExecution">If true, calls await Task.Yield() before writing.</param>
        /// <param name="cancellationToken">An optional cancellation token.</param>
        /// <returns>A task containing the count of items written that completes when all the data has been written to the channel writer.
        /// The count should be ignored if the number of iterations could exceed the max value of long.</returns>
        public static async ValueTask <long> WriteAllLines(this ChannelWriter <string> target,
                                                           TextReader source, bool complete = false, bool deferredExecution = false, CancellationToken cancellationToken = default)
        {
            if (target is null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (source is null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            Contract.EndContractBlock();

            var next = target.WaitToWriteAndThrowIfClosedAsync(ChannelClosedMessage, deferredExecution, cancellationToken);
            await next.ConfigureAwait(false);

            try
            {
                long count = 0;
                var  more  = false;               // if it completed and actually returned false, no need to bubble the cancellation since it actually completed.
                while (!cancellationToken.IsCancellationRequested)
                {
                    var line = await source.ReadLineAsync().ConfigureAwait(false);

                    if (line == null)
                    {
                        more = false;
                        break;
                    }
                    else
                    {
                        more = true;
                    }

                    await next.ConfigureAwait(false);

                    count++;
                    next = target.WriteAsync(line, cancellationToken);
                }
                await next.ConfigureAwait(false);

                if (more)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                }
                return(count);
            }
            catch (Exception ex)
            {
                if (complete)
                {
                    target.Complete(ex);
                    complete = false;
                }
                throw;
            }
            finally
            {
                if (complete)
                {
                    target.Complete();
                }
            }
        }