Пример #1
0
        /// <summary>Gets a Task to represent the asynchronous operation.</summary>
        /// <param name="source">The asynchronous operation.</param>
        /// <param name="cancellationToken">The token used to request cancellation of the asynchronous operation.</param>
        /// <returns>The Task representing the asynchronous operation.</returns>
        public static Task AsTask(this IAsyncAction source, CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            Contract.EndContractBlock();

            // If source is actually a NetFx-to-WinRT adapter, unwrap it instead of creating a new Task:
            var wrapper = source as TaskToAsyncActionAdapter;

            if (wrapper != null && !wrapper.CompletedSynchronously)
            {
                Task innerTask = wrapper.Task;
                Debug.Assert(innerTask != null);
                Debug.Assert(innerTask.Status != TaskStatus.Created);

                if (!innerTask.IsCompleted)
                {
                    // The race here is benign: If the task completes here, the concatination is useless, but not damaging.
                    if (cancellationToken.CanBeCanceled && wrapper.CancelTokenSource != null)
                    {
                        ConcatenateCancelTokens(cancellationToken, wrapper.CancelTokenSource, innerTask);
                    }
                }

                return(innerTask);
            }

            // Fast path to return a completed Task if the operation has already completed:
            switch (source.Status)
            {
            case AsyncStatus.Completed:
                return(Task.CompletedTask);

            case AsyncStatus.Error:
                return(Task.FromException(source.ErrorCode.AttachRestrictedErrorInfo()));

            case AsyncStatus.Canceled:
                return(Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)));
            }

            // Benign race: source may complete here. Things still work, just not taking the fast path.

            // Source is not a NetFx-to-WinRT adapter, but a native future. Hook up the task:
            var bridge = new AsyncInfoToTaskBridge <VoidValueTypeParameter>(cancellationToken);

            try
            {
                source.Completed = new AsyncActionCompletedHandler(bridge.CompleteFromAsyncAction);
                bridge.RegisterForCancellation(source);
            }
            catch
            {
                AsyncCausalitySupport.RemoveFromActiveTasks(bridge.Task);
            }
            return(bridge.Task);
        }
Пример #2
0
        internal AsyncInfoToTaskBridge(CancellationToken cancellationToken)
        {
            if (AsyncCausalitySupport.LoggingOn)
                AsyncCausalitySupport.TraceOperationCreation(this.Task, "WinRT Operation as Task");
            AsyncCausalitySupport.AddToActiveTasks(this.Task);

            _ct = cancellationToken;
        }
Пример #3
0
        /// <summary>Completes the task from the completed asynchronous operation.</summary>
        /// <param name="asyncInfo">The asynchronous operation.</param>
        /// <param name="getResultsFunction">A function used to retrieve the TResult from the async operation; may be null.</param>
        /// <param name="asyncStatus">The status of the asynchronous operation.</param>
        private void Complete(IAsyncInfo asyncInfo, Func <IAsyncInfo, TResult> getResultsFunction, AsyncStatus asyncStatus)
        {
            if (asyncInfo == null)
            {
                throw new ArgumentNullException(nameof(asyncInfo));
            }

            Contract.EndContractBlock();

            AsyncCausalitySupport.RemoveFromActiveTasks(this.Task);

            try
            {
                Debug.Assert(asyncInfo.Status == asyncStatus,
                             "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?");

                // Assuming a correct underlying implementation, the task should not have been
                // completed yet.  If it is completed, we shouldn't try to do any further work
                // with the operation or the task, as something is horked.
                bool taskAlreadyCompleted = Task.IsCompleted;

                Debug.Assert(!taskAlreadyCompleted, "Expected the task to not yet be completed.");

                if (taskAlreadyCompleted)
                {
                    throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                }

                // Clean up our registration with the cancellation token, noting that we're now in the process of cleaning up.
                CancellationTokenRegistration ctr;
                lock (StateLock)
                {
                    _completing = true;
                    ctr         = _ctr; // under lock to avoid torn reads
                    _ctr        = default(CancellationTokenRegistration);
                }
                ctr.TryDeregister(); // It's ok if we end up unregistering a not-initialized registration; it'll just be a nop.

                try
                {
                    // Find out how the async operation completed.  It must be in a terminal state.
                    bool terminalState = asyncStatus == AsyncStatus.Completed ||
                                         asyncStatus == AsyncStatus.Canceled ||
                                         asyncStatus == AsyncStatus.Error;

                    Debug.Assert(terminalState, "The async operation should be in a terminal state.");

                    if (!terminalState)
                    {
                        throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                    }

                    // Retrieve the completion data from the IAsyncInfo.
                    TResult   result = default(TResult);
                    Exception error  = null;
                    if (asyncStatus == AsyncStatus.Error)
                    {
                        error = asyncInfo.ErrorCode;

                        // Defend against a faulty IAsyncInfo implementation:
                        if (error == null)
                        {
                            Debug.Assert(false, "IAsyncInfo.Status == Error, but ErrorCode returns a null Exception (implying S_OK).");
                            error = new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion);
                        }
                        else
                        {
                            error = asyncInfo.ErrorCode.AttachRestrictedErrorInfo();
                        }
                    }
                    else if (asyncStatus == AsyncStatus.Completed && getResultsFunction != null)
                    {
                        try
                        {
                            result = getResultsFunction(asyncInfo);
                        }
                        catch (Exception resultsEx)
                        {
                            // According to the WinRT team, this can happen in some egde cases, such as marshalling errors in GetResults.
                            error       = resultsEx;
                            asyncStatus = AsyncStatus.Error;
                        }
                    }

                    // Nothing to retrieve for a canceled operation or for a completed operation with no result.

                    // Complete the task based on the previously retrieved results:
                    bool success = false;
                    switch (asyncStatus)
                    {
                    case AsyncStatus.Completed:
                        if (AsyncCausalitySupport.LoggingOn)
                        {
                            AsyncCausalitySupport.TraceOperationCompletedSuccess(this.Task);
                        }
                        success = base.TrySetResult(result);
                        break;

                    case AsyncStatus.Error:
                        Debug.Assert(error != null, "The error should have been retrieved previously.");
                        success = base.TrySetException(error);
                        break;

                    case AsyncStatus.Canceled:
                        success = base.TrySetCanceled(_ct.IsCancellationRequested ? _ct : new CancellationToken(true));
                        break;
                    }

                    Debug.Assert(success, "Expected the outcome to be successfully transfered to the task.");
                }
                catch (Exception exc)
                {
                    // This really shouldn't happen, but could in a variety of misuse cases
                    // such as a faulty underlying IAsyncInfo implementation.
                    Debug.Assert(false, string.Format("Unexpected exception in Complete: {0}", exc.ToString()));

                    if (AsyncCausalitySupport.LoggingOn)
                    {
                        AsyncCausalitySupport.TraceOperationCompletedError(this.Task);
                    }

                    // For these cases, store the exception into the task so that it makes its way
                    // back to the caller.  Only if something went horribly wrong and we can't store the exception
                    // do we allow it to be propagated out to the invoker of the Completed handler.
                    if (!base.TrySetException(exc))
                    {
                        Debug.Assert(false, "The task was already completed and thus the exception couldn't be stored.");
                        throw;
                    }
                }
            }
            finally
            {
                // We may be called on an STA thread which we don't own, so make sure that the RCW is released right
                // away. Otherwise, if we leave it up to the finalizer, the apartment may already be gone.
                if (Marshal.IsComObject(asyncInfo))
                {
                    Marshal.ReleaseComObject(asyncInfo);
                }
            }
        } // private void Complete(..)
Пример #4
0
        /// <summary>Gets a Task to represent the asynchronous operation.</summary>
        /// <param name="source">The asynchronous operation.</param>
        /// <param name="cancellationToken">The token used to request cancellation of the asynchronous operation.</param>
        /// <param name="progress">The progress object used to receive progress updates.</param>
        /// <returns>The Task representing the asynchronous operation.</returns>
        public static Task <TResult> AsTask <TResult, TProgress>(this IAsyncOperationWithProgress <TResult, TProgress> source,
                                                                 CancellationToken cancellationToken, IProgress <TProgress> progress)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            Contract.EndContractBlock();

            // If source is actually a NetFx-to-WinRT adapter, unwrap it instead of creating a new Task:
            var wrapper = source as TaskToAsyncOperationWithProgressAdapter <TResult, TProgress>;

            if (wrapper != null && !wrapper.CompletedSynchronously)
            {
                Task <TResult> innerTask = wrapper.Task as Task <TResult>;
                Debug.Assert(innerTask != null);
                Debug.Assert(innerTask.Status != TaskStatus.Created);  // Is WaitingForActivation a legal state at this moment?

                if (!innerTask.IsCompleted)
                {
                    // The race here is benign: If the task completes here, the concatinations are useless, but not damaging.

                    if (cancellationToken.CanBeCanceled && wrapper.CancelTokenSource != null)
                    {
                        ConcatenateCancelTokens(cancellationToken, wrapper.CancelTokenSource, innerTask);
                    }

                    if (progress != null)
                    {
                        ConcatenateProgress(source, progress);
                    }
                }

                return(innerTask);
            }

            // Fast path to return a completed Task if the operation has already completed
            switch (source.Status)
            {
            case AsyncStatus.Completed:
                return(Task.FromResult(source.GetResults()));

            case AsyncStatus.Error:
                return(Task.FromException <TResult>(source.ErrorCode.AttachRestrictedErrorInfo()));

            case AsyncStatus.Canceled:
                return(Task.FromCanceled <TResult>(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)));
            }

            // Benign race: source may complete here. Things still work, just not taking the fast path.

            // Forward progress reports:
            if (progress != null)
            {
                ConcatenateProgress(source, progress);
            }

            // Source is not a NetFx-to-WinRT adapter, but a native future. Hook up the task:
            var bridge = new AsyncInfoToTaskBridge <TResult>(cancellationToken);

            try
            {
                source.Completed = new AsyncOperationWithProgressCompletedHandler <TResult, TProgress>(bridge.CompleteFromAsyncOperationWithProgress);
                bridge.RegisterForCancellation(source);
            }
            catch
            {
                AsyncCausalitySupport.RemoveFromActiveTasks(bridge.Task);
            }
            return(bridge.Task);
        }