From b3b8fb4e629b93c291ac9359042c3560fd73c07d Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Tue, 25 Feb 2025 10:16:34 -0800 Subject: [PATCH 1/6] Update runtime-async.md --- docs/design/specs/runtime-async.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index 48985b139b4cec..2dd77e904bbce5 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -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. +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. By-ref variables may not be hoisted across suspension, and any read of a by-ref variable after suspension will produce null. 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` Other restrictions are likely to be permanent, including * By-ref locals cannot be hoisted across suspension points From 0e4f293789f62bdad282e2ce5aa92a2f7a812682 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Tue, 25 Feb 2025 10:21:17 -0800 Subject: [PATCH 2/6] Better wording --- docs/design/specs/runtime-async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index 2dd77e904bbce5..af4d50ef8e6454 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -46,7 +46,7 @@ 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. By-ref variables may not be hoisted across suspension, and any read of a by-ref variable after suspension will produce null. +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, and any read of a by-ref variable after suspension will produce null. Async methods have some temporary restrictions with may be lifted later: * The `tail` prefix is forbidden From 4ec7146a2c59db61307e97ad524179a7f3776de9 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Tue, 25 Feb 2025 10:30:49 -0800 Subject: [PATCH 3/6] Clarify async method suspension points --- docs/design/specs/runtime-async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index af4d50ef8e6454..42fabc0e257a27 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -23,7 +23,7 @@ Applicability of `MethodImplOptions.Async`: 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` or `ValueTask`. From 61715dba4e32e98c10566d15198ebf13553f74ae Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Tue, 25 Feb 2025 11:45:55 -0800 Subject: [PATCH 4/6] Update docs/design/specs/runtime-async.md Co-authored-by: Jakob Botsch Nielsen --- docs/design/specs/runtime-async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index 42fabc0e257a27..ac852a923f41aa 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -46,7 +46,7 @@ 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. -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, and any read of a by-ref variable after suspension will produce null. +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. Async methods have some temporary restrictions with may be lifted later: * The `tail` prefix is forbidden From a6c901b6bf9ec5986c0d5f6754da504f5ec5701c Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Mon, 24 Mar 2025 13:50:51 -0700 Subject: [PATCH 5/6] Clarify async method restrictions and hoisting rules --- docs/design/specs/runtime-async.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index ac852a923f41aa..a170b6b1213501 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -18,7 +18,7 @@ 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. @@ -46,7 +46,7 @@ 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. -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. +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. Async methods have some temporary restrictions with may be lifted later: * The `tail` prefix is forbidden From 1215225a3628307ea2bca4d29a25246cc82ea3c0 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Tue, 29 Apr 2025 10:04:33 -0700 Subject: [PATCH 6/6] Update docs/design/specs/runtime-async.md Co-authored-by: Vladimir Sadov --- docs/design/specs/runtime-async.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/design/specs/runtime-async.md b/docs/design/specs/runtime-async.md index a170b6b1213501..464f1810ecd731 100644 --- a/docs/design/specs/runtime-async.md +++ b/docs/design/specs/runtime-async.md @@ -46,7 +46,8 @@ 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. -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. +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. +In the same way, pinning locals may not be "hoisted" across suspension points and will have `null` value after a suspension point. Async methods have some temporary restrictions with may be lifted later: * The `tail` prefix is forbidden