Skip to content

Commit 0a97df8

Browse files
333fredjnm2jjonescz
authored
Add proposal for async main codegen update (#9826)
Co-authored-by: Joseph Musser <me@jnm2.com> Co-authored-by: Jan Jones <jan.jones.cz@gmail.com>
1 parent 91e7456 commit 0a97df8

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

proposals/async-main-update.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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

Comments
 (0)