Skip to content

TaskContinuationOptions.RunContinuationsAsync runs continuations inline #26149

@halter73

Description

@halter73

I'm not sure if this is expected/known behavior, but I've found that it's fairly common for TCSs constructed with the TaskContinuationOptions.RunContinuationsAsynchronously option run their continuation inline anyway.

Here's a simple app that repros the issue:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace RunAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            for (var i = 0; i < 1000; i++)
            {
                TestRunAsync(i).Wait();
            }

            Console.WriteLine("Done!");
        }

        static async Task TestRunAsync(int testRun)
        {
            var tcs = new TaskCompletionSource<object>(TaskContinuationOptions.RunContinuationsAsynchronously);
            var mre = new ManualResetEventSlim();

            var tcsAwaiterTask = Task.Run(async () =>
            {
                await tcs.Task;
                if (!mre.Wait(TimeSpan.FromSeconds(10)))
                {
                    Console.WriteLine("Tcs continuation ran inline in test run {0}. Stack trace: {1}", testRun, Environment.StackTrace);
                    throw new Exception();
                }
            });

            var tcsSetterTask = Task.Run(() =>
            {
                tcs.SetResult(null);
                mre.Set();
            });

            await tcsAwaiterTask;
            await tcsSetterTask;
        }
    }
}

Expected output:

Done!

Actual output:

Tcs continuation ran inline in test run 3. Stack trace:    at System.Environment.get_StackTrace()
   at RunAsync.Program.<>c__DisplayClass1_0.<<TestRunAsync>b__0>d.MoveNext() in C:\Users\shalter\Downloads\RunAsync\Program.cs:line 29
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
   at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(IAsyncStateMachineBox box, Boolean allowInlining)
   at System.Threading.Tasks.Task.RunContinuations(Object continuationObject)
   at System.Threading.Tasks.Task`1.TrySetResult(TResult result)
   at System.Threading.Tasks.TaskCompletionSource`1.TrySetResult(TResult result)
   at System.Threading.Tasks.TaskCompletionSource`1.SetResult(TResult result)
   at RunAsync.Program.<>c__DisplayClass1_0.<TestRunAsync>b__1() in C:\Users\shalter\Downloads\RunAsync\Program.cs:line 36
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

Unhandled Exception: System.AggregateException: One or more errors occurred. (Exception of type 'System.Exception' was thrown.) ---> System.Exception: Exception of type 'System.Exception' was thrown.
   at RunAsync.Program.<>c__DisplayClass1_0.<<TestRunAsync>b__0>d.MoveNext() in C:\Users\shalter\Downloads\RunAsync\Program.cs:line 30
--- End of stack trace from previous location where exception was thrown ---
   at RunAsync.Program.TestRunAsync(Int32 testRun) in C:\Users\shalter\Downloads\RunAsync\Program.cs:line 40
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at RunAsync.Program.Main(String[] args) in C:\Users\shalter\Downloads\RunAsync\Program.cs:line 13

This can be reproduced with RC1. @stephentoub

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions