The following is an example of a program that may exhibit stack overflow when using the DependsOnPreviousTaskAsync method, which is surprising because I do not believe that explicit synchronous recursion exists.
You can see an example of a stack before overflow at the breakpoint encoded in the DoSomething method. There will be a large long chain of main dependent async state calls.
The logical chain exists because of the relationship between the task and its predecessor, but I am very surprised that this chain of asynchronous calls appears recursively in the call stack!
To get around the problem, I encoded the method as DependsOnPreviousTaskAsync2 , which uses the old-style ContinueWith continuation handling instead of async / await. In this case, the call stack observed at the breakpoint is never very deep.
My question is: is there something I am missing regarding using async / await that will prevent stack overflows? Or did I just hit the edge register, which requires the use of a workaround to break the awesome recursion inherent in the async / wait state machine?
EDIT : I added TaskContinuationOptions.ExecuteSynchronously to the workaround, and although the observed call stack may be deeper, I don't see any StackOverflowExceptions using this method. Regardless of which discovery logic is successfully applied to ContinueWith , while ExecuteSynchronous not applied in the async / wait version.
class Program { public async static Task<int> DependsOnPreviousTaskAsync(Task<int> previousTask) { if (previousTask == null) return 0; var result = await DoSomethingAsync(previousTask).ConfigureAwait(false); Console.WriteLine(result); return result; } public static Task<int> DependsOnPreviousTaskAsync2(Task<int> previousTask) {
Here is an example call stack at a breakpoint when using DependsOnPreviousTaskAsync :
TestAsyncRecursion.exe!TestAsyncRecursion.Program.DoSomethingAsync(System.Threading.Tasks.Task<int> previousTask) Line 62 C# [Resuming Async Method] mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents<System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>>.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask) Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetResult(System.Threading.Tasks.VoidTaskResult result) Unknown mscorlib.dll!System.Threading.Tasks.Task.WhenAllPromise.Invoke(System.Threading.Tasks.Task completedTask) Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetResult(System.Threading.Tasks.VoidTaskResult result) Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.SetResult(System.Threading.Tasks.VoidTaskResult result) Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult() Unknown TestAsyncRecursion.exe!TestAsyncRecursion.Program.SomethingElseAsync() Line 73 C# [Resuming Async Method] mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents<System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>>.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask) Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageTwo() Unknown mscorlib.dll!System.Threading.Tasks.Task.Finish(bool bUserDelegateExecuted) Unknown mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Unknown mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Unknown mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown [Async Call] TestAsyncRecursion.exe!TestAsyncRecursion.Program.DependsOnPreviousTaskAsync(System.Threading.Tasks.Task<int> previousTask) Line 18 C# [Async Call] TestAsyncRecursion.exe!TestAsyncRecursion.Program.DoSomethingAsync(System.Threading.Tasks.Task<int> previousTask) Line 58 C# [Async Call] TestAsyncRecursion.exe!TestAsyncRecursion.Program.DependsOnPreviousTaskAsync(System.Threading.Tasks.Task<int> previousTask) Line 18 C# [Async Call] TestAsyncRecursion.exe!TestAsyncRecursion.Program.DoSomethingAsync and so on ...
Here is an example call stack at a breakpoint when using DependsOnPreviousTaskAsync2 :
TestAsyncRecursion.exe!TestAsyncRecursion.Program.DoSomethingAsync(System.Threading.Tasks.Task<int> previousTask) Line 62 C# [Resuming Async Method] mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.InvokeMoveNext(object stateMachine) Unknown mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.MoveNextRunner.Run() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.OutputAsyncCausalityEvents<System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>>.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__0() Unknown mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() Unknown mscorlib.dll!System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action action, bool allowInlining, ref System.Threading.Tasks.Task currentTask) Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.VoidTaskResult>.TrySetResult(System.Threading.Tasks.VoidTaskResult result) Unknown mscorlib.dll!System.Threading.Tasks.Task.WhenAllPromise.Invoke(System.Threading.Tasks.Task completedTask) Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishContinuations() Unknown mscorlib.dll!System.Threading.Tasks.Task.FinishStageThree() Unknown mscorlib.dll!System.Threading.Tasks.Task<int>.TrySetResult(int result) Unknown mscorlib.dll!System.Threading.Tasks.TaskCompletionSource<int>.TrySetResult(int result) Unknown TestAsyncRecursion.exe!TestAsyncRecursion.Program.DependsOnPreviousTaskAsync2.AnonymousMethod__4(System.Threading.Tasks.Task<int> t) Line 44 C# mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromResultTask<int>.InnerInvoke() Unknown mscorlib.dll!System.Threading.Tasks.Task.Execute() Unknown mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Unknown mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Unknown mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Unknown mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown