|
2 | 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
3 | 3 |
|
4 | 4 | using System.Diagnostics;
|
| 5 | +using System.Runtime.CompilerServices; |
5 | 6 |
|
6 | 7 | namespace System.Threading.Tasks
|
7 | 8 | {
|
@@ -29,7 +30,7 @@ public static Task Unwrap(this Task<Task> task)
|
29 | 30 | {
|
30 | 31 | if (task == null)
|
31 | 32 | throw new ArgumentNullException("task");
|
32 |
| - |
| 33 | + |
33 | 34 | // Fast path for an already successfully completed outer task: just return the inner one.
|
34 | 35 | // As in the subsequent slower path, a null inner task is special-cased to mean cancellation.
|
35 | 36 | if (task.Status == TaskStatus.RanToCompletion && (task.CreationOptions & TaskCreationOptions.AttachedToParent) == 0)
|
@@ -160,6 +161,29 @@ private static bool TrySetFromTask<TResult>(this TaskCompletionSource<TResult> c
|
160 | 161 | {
|
161 | 162 | Debug.Assert(task.IsCompleted);
|
162 | 163 |
|
| 164 | + // Before transferring the results, check to make sure we're not too deep on the stack. Calling TrySet* |
| 165 | + // will cause any synchronous continuations to be invoked, which is fine unless we're so deep that doing |
| 166 | + // so overflows. ContinueWith has built-in support for avoiding such stack dives, but that support is not |
| 167 | + // (yet) part of await's infrastructure, so until it is we mimic it manually. This matches the behavior |
| 168 | + // employed by the Unwrap implementation in mscorlib. (If TryEnsureSufficientExecutionStack is made public |
| 169 | + // in the future, switch to using it to avoid the try/catch block and exception.) |
| 170 | + try |
| 171 | + { |
| 172 | + RuntimeHelpers.EnsureSufficientExecutionStack(); |
| 173 | + } |
| 174 | + catch (InsufficientExecutionStackException) |
| 175 | + { |
| 176 | + // This is very rare. We're too deep to safely invoke |
| 177 | + // TrySet* synchronously, so do so asynchronously instead. |
| 178 | + Task.Factory.StartNew(s => |
| 179 | + { |
| 180 | + var t = (Tuple<TaskCompletionSource<TResult>, Task>)s; |
| 181 | + TrySetFromTask(t.Item1, t.Item2); |
| 182 | + }, Tuple.Create(completionSource, task), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); |
| 183 | + return true; |
| 184 | + } |
| 185 | + |
| 186 | + // Transfer the results from the supplied Task to the TaskCompletionSource. |
163 | 187 | bool result = false;
|
164 | 188 | switch(task.Status)
|
165 | 189 | {
|
|
0 commit comments