|
| 1 | +# Async main codegen update |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +We update the codegen for `async Task Main` to allow the compiler to use a separate helper from the runtime when present. |
| 6 | + |
| 7 | +## Motivation |
| 8 | + |
| 9 | +In some scenarios, the runtime needs to be able to hook the real `Main` method, not just the `void` or `int` returning method that is generated to |
| 10 | +wrap the user-written `async Task` method. To facilitate this, we allow codegen to use a different method to call the real user-written `Main` method. |
| 11 | + |
| 12 | +## Detailed Design |
| 13 | + |
| 14 | +We update section [§7.1](https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/basic-concepts.md#71-application-startup) of the spec as |
| 15 | +follows: |
| 16 | + |
| 17 | +```diff |
| 18 | +- Otherwise, the synthesized entry point waits for the returned task to complete, calling `GetAwaiter().GetResult()` on the task, using either the parameterless instance method or the extension method described by [§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271). If the task fails, `GetResult()` will throw an exception, and this exception is propagated by the synthesized method. |
| 19 | ++ Otherwise, the synthesized entry point waits for the returned task to complete, either passing the task to `System.Runtime.CompilerServices.AsyncHelpers.HandleAsyncEntryPoint` (if it exists) or calling `GetAwaiter().GetResult()` on the task, using either the parameterless instance method or the extension method described by [§C.3](standard-library.md#c3-standard-library-types-not-defined-in-isoiec-23271). If the task fails, the calling method form will throw an exception, and this exception is propagated by the synthesized method. |
| 20 | +``` |
| 21 | + |
| 22 | +The compiler will look for the following APIs from the core libraries. We do not look at implementations defined outside the core library: |
| 23 | + |
| 24 | +```cs |
| 25 | +namespace System.Runtime.CompilerServices; |
| 26 | + |
| 27 | +public class AsyncHelpers |
| 28 | +{ |
| 29 | + public static void HandleAsyncEntryPoint(System.Threading.Tasks.Task task); |
| 30 | + public static int HandleAsyncEntryPoint(System.Threading.Tasks.Task<int> task); |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +When present the resulting transformation looks like this: |
| 35 | + |
| 36 | +```cs |
| 37 | +// Original code |
| 38 | +public class C |
| 39 | +{ |
| 40 | + public static async Task Main() |
| 41 | + { |
| 42 | + await System.Threading.Tasks.Task.Yield(); |
| 43 | + Console.WriteLine("Hello world"); |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +// Lowered pseudocode, not including async state machine if present |
| 48 | +public class C |
| 49 | +{ |
| 50 | + public static void $<Main>() |
| 51 | + { |
| 52 | + System.Runtime.CompilerServices.AsyncHelpers.HandleAsyncEntryPoint(Main()); |
| 53 | + } |
| 54 | + |
| 55 | + public static async Task Main() |
| 56 | + { |
| 57 | + await System.Threading.Tasks.Task.Yield(); |
| 58 | + Console.WriteLine("Hello world"); |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +See also the approved API discussion in https://github.com/dotnet/runtime/issues/121046. |
0 commit comments