Пример #1
0
        /// <summary>
        /// Asynchronously writes a sequence of bytes to a stream, advances the position within the stream by the number of bytes written,
        /// and monitors cancellation requests.
        /// </summary>
        /// <remarks>
        /// <para>Use the <see cref="Stream.CanWrite"/> property to determine whether the stream instance supports writing.</para>
        /// <para>
        /// If the operation is canceled before it completes, the returned task contains the <see cref="TaskStatus.Canceled"/>
        /// value for the <see cref="Task.Status"/> property.
        /// </para>
        /// </remarks>
        /// <param name="stream">The stream to write data to.</param>
        /// <param name="buffer">The buffer to read the data from.</param>
        /// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param>
        /// <param name="count">The maximum number of bytes to write.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        /// <returns>
        /// A task that represents the asynchronous write operation.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para>If <paramref name="stream"/> is <see langword="null"/>.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="buffer"/> is <see langword="null"/>.</para>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <para>If <paramref name="offset"/> is negative.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="count"/> is negative.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para>If the sum of <paramref name="offset"/> and <paramref name="count"/> is larger than the buffer length.</para>
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// <para>If <paramref name="stream"/> does not support writing.</para>
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// <para>If <paramref name="stream"/> has been disposed.</para>
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// <para>If <paramref name="stream"/> is currently in use by a previous write operation.</para>
        /// </exception>
        public static Task WriteAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            if (cancellationToken.IsCancellationRequested)
                return CompletedTask.Canceled();

#if NET45PLUS
            // This code requires the `Stream` class provide an implementation of `WriteAsync`. The unit tests will
            // detect any case where this results in a stack overflow.
            return stream.WriteAsync(buffer, offset, count, cancellationToken);
#else
            return Task.Factory.FromAsync(stream.BeginWrite, stream.EndWrite, buffer, offset, count, null);
#endif
        }
Пример #2
0
        /// <summary>
        /// Asynchronously clears all buffers for a stream and causes any buffered data to be written to the underlying device,
        /// and monitors cancellation requests.
        /// </summary>
        /// <remarks>
        /// <para>If the operation is canceled before it completes, the returned task contains the <see cref="TaskStatus.Canceled"/>
        /// value for the <see cref="Task.Status"/> property.</para>
        /// <para>
        /// If a derived class does not flush the buffer in its implementation of the <see cref="Stream.Flush()"/> method,
        /// the <see cref="FlushAsync(Stream)"/> method will not flush the buffer.
        /// </para>
        /// </remarks>
        /// <param name="stream">The stream to flush.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        /// <returns>A task that represents the asynchronous flush operation.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="stream"/> is <see langword="null"/>.</exception>
        /// <exception cref="ObjectDisposedException">If <paramref name="stream"/> has been disposed.</exception>
        public static Task FlushAsync(this Stream stream, CancellationToken cancellationToken)
        {
            if (stream == null)
                throw new ArgumentNullException("stream");

            if (cancellationToken.IsCancellationRequested)
                return CompletedTask.Canceled();

#if NET45PLUS
            // This code requires the `Stream` class provide an implementation of `FlushAsync`. The unit tests will
            // detect any case where this results in a stack overflow.
            return stream.FlushAsync(cancellationToken);
#else
            return Task.Factory.StartNew(state => ((Stream)state).Flush(), stream, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
#endif
        }
        /// <summary>
        /// Asynchronously reads a sequence of bytes from a stream, advances the position within the stream by the number of bytes read,
        /// and monitors cancellation requests.
        /// </summary>
        /// <remarks>
        /// <para>Use the <see cref="Stream.CanRead"/> property to determine whether the stream instance supports
        /// reading.</para>
        /// <para>
        /// If the operation is canceled before it completes, the returned task contains the <see cref="TaskStatus.Canceled"/>
        /// value for the <see cref="Task.Status"/> property.
        /// </para>
        /// </remarks>
        /// <param name="stream">The stream to read data from.</param>
        /// <param name="buffer">The buffer to write the data into.</param>
        /// <param name="offset">The byte offset in <paramref name="buffer"/> at which to begin writing data from the stream.</param>
        /// <param name="count">The maximum number of bytes to read.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        /// <returns>
        /// A task that represents the asynchronous read operation. When the task completes successfully, the <see cref="Task{TResult}.Result"/>
        /// property contains the total number of bytes read into the buffer. The result value can be less than the number of bytes requested if
        /// the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <para>If <paramref name="stream"/> is <see langword="null"/>.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="buffer"/> is <see langword="null"/>.</para>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <para>If <paramref name="offset"/> is negative.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="count"/> is negative.</para>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <para>If the sum of <paramref name="offset"/> and <paramref name="count"/> is larger than the buffer length.</para>
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// <para>If <paramref name="stream"/> does not support reading.</para>
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// <para>If <paramref name="stream"/> has been disposed.</para>
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// <para>If <paramref name="stream"/> is currently in use by a previous read operation.</para>
        /// </exception>
        public static Task <int> ReadAsync(this Stream stream, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(CompletedTask.Canceled <int>());
            }

#if NET45PLUS
            // This code requires the `Stream` class provide an implementation of `ReadAsync`. The unit tests will
            // detect any case where this results in a stack overflow.
            return(stream.ReadAsync(buffer, offset, count, cancellationToken));
#else
            return(Task <int> .Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, offset, count, null));
#endif
        }
        /// <summary>
        /// Asynchronously reads the bytes from a source stream and writes them to a destination stream,
        /// using a specified buffer size and cancellation token.
        /// </summary>
        /// <remarks>
        /// <para>If the operation is canceled before it completes, the returned task contains the <see cref="TaskStatus.Canceled"/>
        /// value for the <see cref="Task.Status"/> property.</para>
        /// <para>
        /// Copying begins at the current position in <paramref name="stream"/>.
        /// </para>
        /// </remarks>
        /// <param name="stream">The source stream.</param>
        /// <param name="destination">The stream to which the contents of the source stream will be copied.</param>
        /// <param name="bufferSize">The size, in bytes, of the buffer. This value must be greater than zero. The default size is 81920.</param>
        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
        /// <returns>A task that represents the asynchronous copy operation.</returns>
        /// <exception cref="ArgumentNullException">
        /// <para>If <paramref name="stream"/> is <see langword="null"/>.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="destination"/> is <see langword="null"/>.</para>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <para>If <paramref name="bufferSize"/> is negative or zero.</para>
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// <para>If <paramref name="stream"/> is disposed.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="destination"/> is disposed.</para>
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// <para>If <paramref name="stream"/> does not support reading.</para>
        /// <para>-or-</para>
        /// <para>If <paramref name="destination"/> does not support writing.</para>
        /// </exception>
        public static Task CopyToAsync(this Stream stream, Stream destination, int bufferSize, CancellationToken cancellationToken)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }
            if (!stream.CanRead)
            {
                throw new NotSupportedException("The stream does not support reading");
            }
            if (!destination.CanWrite)
            {
                throw new NotSupportedException("The destination does not support writing");
            }
            if (bufferSize <= 0)
            {
                throw new ArgumentOutOfRangeException("bufferSize");
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(CompletedTask.Canceled());
            }

#if NET45PLUS
            // This code requires the `Stream` class provide an implementation of `CopyToAsync`. The unit tests will
            // detect any case where this results in a stack overflow.
            return(stream.CopyToAsync(destination, bufferSize, cancellationToken));
#else
            return(CopyToAsync(stream, destination, new byte[bufferSize], cancellationToken));
#endif
        }
Пример #5
0
        /// <summary>
        /// Creates a task that will complete after a time delay.
        /// </summary>
        /// <remarks>
        /// If the cancellation token is signaled before the specified time delay, then the task
        /// is completed in <see cref="TaskStatus.Canceled"/> state. Otherwise, the task is
        /// completed in <see cref="TaskStatus.RanToCompletion"/> state once the specified time
        /// delay has expired.
        /// <para>This method ignores any fractional milliseconds when evaluating <paramref name="delay"/>.</para>
        /// </remarks>
        /// <param name="delay">The time span to wait before completing the returned task</param>
        /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned task</param>
        /// <returns>A task that represents the time delay</returns>
        /// <exception cref="ArgumentOutOfRangeException">If <paramref name="delay"/> represents a negative time interval.</exception>
        /// <exception cref="TaskCanceledException">If the task has been canceled.</exception>
        /// <exception cref="ObjectDisposedException">If the provided <paramref name="cancellationToken"/> has already been disposed.</exception>
        public static Task Delay(TimeSpan delay, CancellationToken cancellationToken)
        {
#if NET45PLUS
            return(Task.Delay(delay, cancellationToken));
#else
            long totalMilliseconds = (long)delay.TotalMilliseconds;
            if (totalMilliseconds < 0)
            {
                throw new ArgumentOutOfRangeException("delay");
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(CompletedTask.Canceled());
            }

            if (totalMilliseconds == 0)
            {
                return(CompletedTask.Default);
            }

            TaskCompletionSource <VoidResult> result = new TaskCompletionSource <VoidResult>();

#if !PORTABLE
            TaskCompletionSource <VoidResult> intermediateResult = new TaskCompletionSource <VoidResult>();

            RegisteredWaitHandle timerRegisteredWaitHandle        = null;
            RegisteredWaitHandle cancellationRegisteredWaitHandle = null;

            WaitOrTimerCallback timedOutCallback =
                (object state, bool timedOut) =>
            {
                if (timedOut)
                {
                    intermediateResult.TrySetResult(default(VoidResult));
                }
            };

            IAsyncResult asyncResult = intermediateResult.Task;
            timerRegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, timedOutCallback, null, delay, true);

            if (cancellationToken.CanBeCanceled)
            {
                WaitOrTimerCallback cancelledCallback =
                    (object state, bool timedOut) =>
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        intermediateResult.TrySetCanceled();
                    }
                };

                cancellationRegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(cancellationToken.WaitHandle, cancelledCallback, null, Timeout.Infinite, true);
            }

            intermediateResult.Task
            .ContinueWith(
                _ =>
            {
                if (cancellationRegisteredWaitHandle != null)
                {
                    cancellationRegisteredWaitHandle.Unregister(null);
                }

                if (timerRegisteredWaitHandle != null)
                {
                    timerRegisteredWaitHandle.Unregister(null);
                }
            }, TaskContinuationOptions.ExecuteSynchronously)
            .ContinueWith(
                cleanupTask =>
            {
                switch (cleanupTask.Status)
                {
                case TaskStatus.RanToCompletion:
                    result.SetFromTask(intermediateResult.Task);
                    break;

                case TaskStatus.Canceled:
                    result.SetCanceled();
                    break;

                case TaskStatus.Faulted:
                    result.SetException(cleanupTask.Exception.InnerExceptions);
                    break;

                default:
                    throw new InvalidOperationException("Unreachable");
                }
            });
#else
            // Since portable-net40 doesn't provide Task.Delay and also doesn't provide ThreadPool.RegisterWaitForSingleObject,
            // we need to implement this functionality using timers stored in a ConditionalWeakTable, which are associated with
            // the actual Task instance that gets returned by this method.

            CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration);
            Timer timer = null;

            TimerCallback timerCallback =
                state =>
            {
                result.TrySetResult(default(VoidResult));
                timer.Dispose();
                cancellationTokenRegistration.Dispose();
            };

            timer = new Timer(timerCallback, null, Timeout.Infinite, Timeout.Infinite);
            _delayTimers.Add(result.Task, timer);
            timer.Change(delay, TimeSpan.FromMilliseconds(-1));


            if (cancellationToken.CanBeCanceled)
            {
                Action cancellationCallback =
                    () =>
                {
                    result.TrySetCanceled();

                    if (timer != null)
                    {
                        timer.Dispose();
                    }

                    cancellationTokenRegistration.Dispose();
                };

                cancellationTokenRegistration = cancellationToken.Register(cancellationCallback);
            }
#endif

            return(result.Task);
#endif
        }
Пример #6
0
        /// <summary>
        /// Creates a task that will complete when all of the supplied tasks have completed.
        /// </summary>
        /// <remarks>
        /// If any of the supplied tasks completes in a faulted state, the returned task will also
        /// complete in a <see cref="TaskStatus.Faulted"/> state, where its exceptions will contain
        /// the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
        ///
        /// <para>If none of the supplied tasks faulted but at least one of them was canceled, the
        /// returned task will end in the <see cref="TaskStatus.Canceled"/> state.</para>
        ///
        /// <para>If none of the tasks faulted and none of the tasks were canceled, the resulting
        /// task will end in the <see cref="TaskStatus.RanToCompletion"/> state. The
        /// <see cref="Task{TResult}.Result"/> of the returned task will be set to an array
        /// containing all of the results of the supplied tasks in the same order as they were
        /// provided (e.g. if the input tasks array contained t1, t2, t3, the output task's
        /// <see cref="Task{TResult}.Result"/> will return an <typeparamref name="TResult"/>[]
        /// where <c>arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result</c>).</para>
        ///
        /// <para>If the supplied array/enumerable contains no tasks, the returned task will
        /// immediately transition to a <see cref="TaskStatus.RanToCompletion"/> state before it's
        /// returned to the caller. The returned <typeparamref name="TResult"/>[] will be an array
        /// of 0 elements.</para>
        /// </remarks>
        /// <typeparam name="TResult">The type of the completed task.</typeparam>
        /// <param name="tasks">The tasks to wait on for completion.</param>
        /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
        /// <exception cref="ArgumentNullException">If <paramref name="tasks"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="tasks"/> contains any <see langword="null"/> values.</exception>
        public static Task <TResult[]> WhenAll <TResult>(IEnumerable <Task <TResult> > tasks)
        {
#if NET45PLUS
            return(Task.WhenAll(tasks));
#else
            if (tasks == null)
            {
                throw new ArgumentNullException("tasks");
            }

            Task <TResult>[] tasksArray = tasks.ToArray();
            if (tasksArray.Length == 0)
            {
                return(CompletedTask.FromResult(new TResult[0]));
            }

            if (tasksArray.Contains(null))
            {
                throw new ArgumentException("tasks cannot contain any null values", "tasks");
            }

            TaskCompletionSource <TResult[]> taskCompletionSource = new TaskCompletionSource <TResult[]>();
            Action <Task <TResult>[]>        continuationAction   =
                completedTasks =>
            {
                List <TResult>   results    = new List <TResult>();
                List <Exception> exceptions = new List <Exception>();
                bool             canceled   = false;

                foreach (var completedTask in completedTasks)
                {
                    switch (completedTask.Status)
                    {
                    case TaskStatus.RanToCompletion:
                        results.Add(completedTask.Result);
                        break;

                    case TaskStatus.Canceled:
                        canceled = true;
                        break;

                    case TaskStatus.Faulted:
                        exceptions.AddRange(completedTask.Exception.InnerExceptions);
                        break;

                    default:
                        throw new InvalidOperationException("Unreachable");
                    }
                }

                if (exceptions.Count > 0)
                {
                    taskCompletionSource.SetException(exceptions);
                }
                else if (canceled)
                {
                    taskCompletionSource.SetCanceled();
                }
                else
                {
                    taskCompletionSource.SetResult(results.ToArray());
                }
            };
            Task.Factory.ContinueWhenAll(tasksArray, continuationAction);
            return(taskCompletionSource.Task);
#endif
        }