protected virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContextBase, AsyncCallback callback, object state) { HttpRequestMessage request = ConvertRequest(httpContextBase); // Add route data request.Properties[HttpPropertyKeys.HttpRouteDataKey] = _routeData; Task responseBodyTask = _server.Value.SendAsync(request, CancellationToken.None) .Then(response => ConvertResponse(httpContextBase, response, request)) .FastUnwrap(); TaskWrapperAsyncResult result = new TaskWrapperAsyncResult(responseBodyTask, state); if (callback != null) { if (result.IsCompleted) { // If the underlying task is already finished, from our caller's perspective this is just // a synchronous completion. See also DevDiv #346170. result.CompletedSynchronously = true; callback(result); } else { // If the underlying task isn't yet finished, from our caller's perspective this will be // an asynchronous completion. We'll use ContinueWith instead of Finally for two reasons: // // - Finally propagates the antecedent Task's exception, which we don't need to do here. // Out caller will eventually call EndProcessRequest, which correctly observes the // antecedent Task's exception anyway if it faulted. // // - Finally invokes the callback on the captured SynchronizationContext, which is // unnecessary when using APM (Begin / End). APM assumes that the callback is invoked // on an arbitrary ThreadPool thread with no SynchronizationContext set up, so // ContinueWith gets us closer to the desired semantic. // // There is still a race here: the Task might complete after the IsCompleted check above, // so the callback might be invoked on another thread concurrently with the original // thread's call to BeginProcessRequest. But we shouldn't concern ourselves with that; // the caller has to be prepared for that possibility and do the right thing. We also // don't need to worry about the callback throwing since the caller should give us a // callback which is well-behaved. result.CompletedSynchronously = false; responseBodyTask.ContinueWith(_ => { callback(result); }); } } return(result); }
/// <summary> /// Provides an asynchronous process End method when the process ends. /// </summary> /// <param name="result">An <see cref="T:System.IAsyncResult"/> that contains information about the status of the process.</param> protected virtual void EndProcessRequest(IAsyncResult result) { TaskWrapperAsyncResult asyncResult = (TaskWrapperAsyncResult)result; Contract.Assert(asyncResult != null); Task task = asyncResult.Task; // Check task result and unwrap any exceptions if (task.IsCanceled) { throw Error.OperationCanceled(); } else if (task.IsFaulted) { throw task.Exception.GetBaseException(); } }
protected virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContextBase, AsyncCallback callback, object state) { HttpRequestMessage request = ConvertRequest(httpContextBase); // Add route data request.Properties[HttpPropertyKeys.HttpRouteDataKey] = _routeData; Task responseBodyTask = _server.Value.SendAsync(request, CancellationToken.None) .Then(response => ConvertResponse(httpContextBase, response, request)) .FastUnwrap(); TaskWrapperAsyncResult result = new TaskWrapperAsyncResult(responseBodyTask, state); if (callback != null) { if (result.IsCompleted) { // If the underlying task is already finished, from our caller's perspective this is just // a synchronous completion. See also DevDiv #346170. result.CompletedSynchronously = true; callback(result); } else { // If the underlying task isn't yet finished, from our caller's perspective this will be // an asynchronous completion. We'll use ContinueWith instead of Finally for two reasons: // // - Finally propagates the antecedent Task's exception, which we don't need to do here. // Out caller will eventually call EndProcessRequest, which correctly observes the // antecedent Task's exception anyway if it faulted. // // - Finally invokes the callback on the captured SynchronizationContext, which is // unnecessary when using APM (Begin / End). APM assumes that the callback is invoked // on an arbitrary ThreadPool thread with no SynchronizationContext set up, so // ContinueWith gets us closer to the desired semantic. // // There is still a race here: the Task might complete after the IsCompleted check above, // so the callback might be invoked on another thread concurrently with the original // thread's call to BeginProcessRequest. But we shouldn't concern ourselves with that; // the caller has to be prepared for that possibility and do the right thing. We also // don't need to worry about the callback throwing since the caller should give us a // callback which is well-behaved. result.CompletedSynchronously = false; responseBodyTask.ContinueWith(_ => { callback(result); }); } } return result; }