1 //------------------------------------------------------------------------------
2 // <copyright file="TaskAsyncHelper.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
8 * Assists in converting an method written using the Task Asynchronous Pattern to a Begin/End method pair.
10 * Copyright (c) 2010 Microsoft Corporation
13 namespace System.Web {
15 using System.Threading.Tasks;
17 internal static class TaskAsyncHelper {
19 private static readonly Task s_completedTask = Task.FromResult<object>(null);
21 internal static IAsyncResult BeginTask(Func<Task> taskFunc, AsyncCallback callback, object state) {
22 Task task = taskFunc();
24 // Something went wrong - let our caller handle it.
28 // We need to wrap the inner Task so that the IAsyncResult exposed by this method
29 // has the state object that was provided as a parameter. We could be a bit smarter
30 // about this to save an allocation if the state objects are equal, but that's a
31 // micro-optimization.
32 TaskWrapperAsyncResult resultToReturn = new TaskWrapperAsyncResult(task, state);
34 // Task instances are always marked CompletedSynchronously = false, even if the
35 // operation completed synchronously. We should detect this and modify the IAsyncResult
36 // we pass back to our caller as appropriate. Only read the 'IsCompleted' property once
37 // to avoid a race condition where the underlying Task completes during this method.
38 bool actuallyCompletedSynchronously = task.IsCompleted;
39 if (actuallyCompletedSynchronously) {
40 resultToReturn.ForceCompletedSynchronously();
43 if (callback != null) {
44 // ContinueWith() is a bit slow: it captures execution context and hops threads. We should
45 // avoid calling it and just invoke the callback directly if the underlying Task is
46 // already completed. Only use ContinueWith as a fallback. There's technically a ---- here
47 // in that the Task may have completed between the check above and the call to
48 // ContinueWith below, but ContinueWith will do the right thing in both cases.
49 if (actuallyCompletedSynchronously) {
50 callback(resultToReturn);
53 task.ContinueWith(_ => callback(resultToReturn));
57 return resultToReturn;
60 // The parameter is named 'ar' since it matches the parameter name on the EndEventHandler delegate type,
61 // and we expect that most consumers will end up invoking this method via an instance of that delegate.
62 internal static void EndTask(IAsyncResult ar) {
64 throw new ArgumentNullException("ar");
67 // Make sure the incoming parameter is actually the correct type.
68 TaskWrapperAsyncResult taskWrapper = ar as TaskWrapperAsyncResult;
69 if (taskWrapper == null) {
71 throw new ArgumentException(SR.GetString(SR.TaskAsyncHelper_ParameterInvalid), "ar");
74 // The End* method doesn't actually perform any actual work, but we do need to maintain two invariants:
75 // 1. Make sure the underlying Task actually *is* complete.
76 // 2. If the Task encountered an exception, observe it here.
77 // (TaskAwaiter.GetResult() handles both of those, and it rethrows the original exception rather than an AggregateException.)
78 taskWrapper.Task.GetAwaiter().GetResult();
81 internal static Task CompletedTask {
83 return s_completedTask;