-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Remove HoistedLocal from runtime-async spec #112918
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
b3b8fb4
0e4f293
4ec7146
61715db
a6c901b
1215225
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,12 +18,12 @@ Applicability of `MethodImplOptions.Async`: | |
| * The `[MethodImpl(MethodImplOptions.Async)]` only has effect when applied to method definitions with CIL implementation. | ||
| * Async method definitions are only valid inside async-capable assemblies. An async-capable assembly is one which references a corlib containing an `abstract sealed class RuntimeFeature` with a `public const string` field member named `Async`. | ||
| * Combining `MethodImplOptions.Async` with `MethodImplOptions.Synchronized` is invalid. | ||
| * Applying `MethodImplOptions.Async` to methods with `byref` or `ref-like` parameters is invalid. | ||
| * Applying `MethodImplOptions.Async` to methods with a `byref` or `ref-like` return value is invalid. | ||
| * Applying `MethodImplOptions.Async` to vararg methods is invalid. | ||
|
|
||
| Sync methods are all other methods. | ||
|
|
||
| Unlike sync methods, async methods support suspension. Suspension allows async methods to yield control flow back to their caller at certain well-defined suspension points, and resume execution of the remaining method at a later time or location, potentially on another thread. | ||
| Unlike sync methods, async methods support suspension. Suspension allows async methods to yield control flow back to their caller at certain well-defined suspension points, and resume execution of the remaining method at a later time or location, potentially on another thread. Suspension points are where suspension may occur, but suspension is not required if all Task-like objects are completed. | ||
|
|
||
| Async methods also do not have matching return type conventions as sync methods. For sync methods, the stack should contain a value convertible to the stated return type before the `ret` instruction. For async methods, the stack should be empty in the case of `Task` or `ValueTask`, or the type argument in the case of `Task<T>` or `ValueTask<T>`. | ||
|
|
||
|
|
@@ -46,13 +46,11 @@ Async methods support the following suspension points: | |
|
|
||
| Each of the above methods will have semantics analogous to the current AsyncTaskMethodBuilder.AwaitOnCompleted/AwaitUnsafeOnCompleted methods. After calling this method, it can be presumed that the task has completed. | ||
|
|
||
| Only local variables which are "hoisted" may be used across suspension points. That is, only "hoisted" local variables will have their state preserved after returning from a suspension. On methods with the `localsinit` flag set, non-"hoisted" local variables will be initialized to their default value when resuming from suspension. Otherwise, these variables will have an undefined value. To identify "hoisted" local variables, they must have an optional custom modifier to the `System.Runtime.CompilerServices.HoistedLocal` class, which will be a new .NET runtime API. This custom modifier must be the last custom modifier on the variable. It is invalid for by-ref variables, or variables with a by-ref-like type, to be marked hoisted. Hoisted local variables are stored in managed memory and cannot be converted to unmanaged pointers without explicit pinning. | ||
| The code generator is free to ignore the `HoistedLocal` modifier if it can prove that this makes no observable difference in the execution of the generated program. This can be observable in diagnostics since it may mean the value of a local with the `HoistedLocal` modifier will not be available after certain suspension points. | ||
| Local variables used across suspension points are considered "hoisted." That is, only "hoisted" local variables will have their state preserved after returning from a suspension. By-ref variables may not be hoisted across suspension points, and any read of a by-ref variable after a suspension point will produce null. Structs containing by-ref variables will also not be hoisted across suspension points and will have their default value after a suspension point. | ||
agocke marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Async methods have some temporary restrictions with may be lifted later: | ||
| * The `tail` prefix is forbidden | ||
| * Usage of the `localloc` instruction is forbidden | ||
| * Pinning locals may not be marked `HoistedLocal` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we have a similar rule that locals marked pinned are zeroed at suspension points? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Lets break pinned locals in the same way as byrefs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typically a pinned local is an internal pointer ( But object references (i.e. |
||
|
|
||
| Other restrictions are likely to be permanent, including | ||
| * By-ref locals cannot be hoisted across suspension points | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we rather have an item that clarifies that the return type must be
Task/ValueTask/Task<T>/ValueTask<T>? The fact thatTcannot be a byref type falls out naturally from the constraints onT.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can be explicit here that the return value cannot be byref. - so no ambiguities whether
ref Task foo()is allowed or not.The part that return type cannot be ref-like kind of follows from requiring Task or ValueTask, but does not seem to hurt to explicitly mention that, so I think it is ok either way.