-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Rename AsyncMethodKind enum values: NotAsync→Ordinary, RuntimeAsync→TaskReturningILAsync #121649
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
base: main
Are you sure you want to change the base?
Conversation
|
Tagging subscribers to this area: @mangod9 |
|
TODO: this may need to run CI with commits from #119420 - to see if everything works with async enabled. Those commits would need to be reverted once we ready to merge. (we are not yet enabling async in the main) |
|
CC: @jakobbotsch @agocke - mostly an FYI on renames. |
| #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1) | ||
|
|
||
| enum class AsyncMethodKind | ||
| { |
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.
Would it be better to make this a bit field?
- ReturnsTaskOrValueTask = 0x1, // The method returns Task or ValueTask
- Async = 0x2, // The method IL in metadata has MethodImplAttributes.Async
- AsyncCall = 0x4, // The entrypoint of this method has async calling convention
I took the bit names from the managed type system. I am not attached to these names. If we come up with different names, we should update the managed type system to match.
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.
Would it be better to make this a bit field?
Possibly. While it may be difficult to name combinations of traits, each individual trait seems easier to name.
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'd prefer something like ILAsync for the presence of the MethodImpl, or MethodImplAsync if we go for more verbose names.
Most of the runtime uses Async/ASYNC to mean "async callconv" or "emitted as state machine" and that matters in a lot more places than the places that care about the methodimpl, which is basically just the part that selects who owns the actual IL.
Thus I think the shorter Async should map to the runtime meaning of Async.
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.
Maybe we do not even need the flag for MethodImplAsync once we sort out which variant is a thunk and which is not.
Let me think/try if we can have just:
- ReturnsTaskOrValueTask // returns a promise type, not Async (in runtime sense)
- Async // the body is a state machine, assumes async callconv
- Variant // method has two variants and this is one of them
- Thunk // has synthetic body, which forwards to the other variant
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.
Managed type system may still need something for MethodImplAttributes.Async as a part of object model.
Also in the managed typesystem it is a bit more obvious when things represent metadata/ECMA entities vs. something that models the actual execution. So the managed type system could as well use Async for the methodimpl, maybe..., If not too confusing.
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.
each individual trait seems easier to name.
Yep, individual traits are easier to name and explain.
MethodImplAsync if we go for more verbose names.
More verbose self-describing names are good to avoid confusion. MethodImplAsync flag / IsMethodImplAsync would be fine with me.
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.
MethodImplAsync
The managed type system has similar prior art: The property for MethodImplAttributes.Runtime is called MethodDesc.IsRuntimeImplemented since MethodDesc.IsRuntime would look odd.
src/coreclr/vm/method.hpp
Outdated
| // Note: Generic T-returning methods are Ordinary, even if T could be a Task. | ||
| Ordinary, | ||
|
|
||
| // Regular methods that return Task/ValueTask |
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.
AsyncMethodKind.TaskReturning is the name that I think is important to change to eliminate ambiguity.
#119565 (comment)
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.
AsyncMethodKind.TaskReturning literally means "a method whose definition returns a Task (or ValueTask)". Hard to come up with a better name for the concept.
The MethodImpl.Async only tells how to interpret the IL inside the method. The definition still returns a Task.
Maybe it will become more clear if it is a bit in a bitfield.
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.
"TaskReturning" is an equally accurate description for AsyncMethodKind.RuntimeAsync which are also methods that return a Task. AsyncMethodKind.TaskReturning is not refering to all TaskReturning methods, only to a particular subset of them. I was trying to pick a name which is narrow enough that it doesn't include any methods outside of the intended subset.
I'm just fine with the bitfield Jan suggested as it also avoids the ambiguity around the term Task Returning.
…>TaskReturningILAsync Co-authored-by: VSadov <[email protected]>
6fd26bf to
7f7870f
Compare
|
|
||
| // Runtime-async | ||
| RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 0, "Enables runtime async method support") | ||
| RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 1, "Enables runtime async method support") |
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.
TODO: undo the commit that enables async feature before merging
| #define METHOD_TOKEN_RANGE_BIT_COUNT (24 - METHOD_TOKEN_REMAINDER_BIT_COUNT) | ||
| #define METHOD_TOKEN_RANGE_MASK ((1 << METHOD_TOKEN_RANGE_BIT_COUNT) - 1) | ||
|
|
||
| enum class AsyncMethodKind |
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 this be called AsyncMethodFlags now that it is a collection of flags and not an enumeration of all possible kinds?
| // we cannot resolve to an Async variant in such case. | ||
| // return NULL, so that caller would re-resolve as a regular method call | ||
| pMD = pMD->IsTaskReturningMethod() ? | ||
| pMD = pMD->IsTaskOrValueTaskReturningMethod() ? |
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.
We should unify on TaskOrValueTaskReturningMethod or ReturnsTaskOrValueTask everywhere.
src/coreclr/vm/method.hpp
Outdated
| // which owns the original IL. (AsyncVariantImpl) | ||
| RuntimeAsync, | ||
| // Method body is emitted as a state machine and uses Async call convention. | ||
| Async = 1, |
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.
We should unify on one of Async or AsyncCall to indentify async calling convention everywhere.
src/coreclr/vm/method.hpp
Outdated
| Async = 1, | ||
| // Not Async method that returns a promise type {Task, Task<T>, ValueTask, ValueTask<T>} | ||
| ReturnsTaskOrValueTask = 2, | ||
| // Method has two variants and this is one of them. |
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.
| // Method has two variants and this is one of them. | |
| // MethodDesc represents async calling convention entrypoint of a Task-returning method. |
Can the description of AsyncVariant be more precise?
src/coreclr/vm/method.hpp
Outdated
|
|
||
| // Is this a Variant method? | ||
| // If yes, the method has another variant. | ||
| inline bool IsVariantMethod() const |
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 do not understand how this is different from IsAsyncVariant.
Also, "Variant" without "Async" it makes it look like it has something to do with type variance that is completely different feature.
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.
Variant flag only tells that this is one from a pair. Basically means we can call GetAsyncOtherVariant and get the other variant.
Perhaps HasAsyncOtherVariant is a better name. - it would match GetAsyncOtherVariant.
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.
If we standardize on AsyncCall to mean async calling convention, I think a better names would be IsAsyncCallVariant / GetAsyncCallVariant. They make it clear that it is about the entrypoint with async calling convention.
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.
standardize on AsyncCall to mean async calling convention
If we were to standardize on AsyncCallConv to mean async calling convention, this would be IsAsyncCallConvVariant / GetAsyncCallConvVariant
| { | ||
| return GetAsyncMethodKind() == AsyncMethodKind::AsyncVariantThunk || | ||
| GetAsyncMethodKind() == AsyncMethodKind::AsyncVariantImpl; | ||
| return hasAsyncKindFlags(GetAsyncMethodKind(), AsyncMethodKind::Async | AsyncMethodKind::HasAsyncOtherVariant); |
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.
| return hasAsyncKindFlags(GetAsyncMethodKind(), AsyncMethodKind::Async | AsyncMethodKind::HasAsyncOtherVariant); | |
| return hasAsyncKindFlags(GetAsyncMethodKind(), AsyncMethodKind::HasAsyncOtherVariant); |
Does this need to test AsyncMethodKind::Async bit?
The term "TaskReturning" was overloaded, creating confusion between the enum value
AsyncMethodKind::TaskReturningand the concept of "task-returning methods" more broadly.Changes
AsyncMethodKind::NotAsync→AsyncMethodKind::OrdinaryClearer name for regular methods not involved in async functionality
AsyncMethodKind::RuntimeAsync→AsyncMethodKind::TaskReturningILAsyncExplicitly indicates task-returning methods marked with
MethodImpl::Asyncin metadataUpdated comments to reflect new naming, particularly the relationship between
{TaskReturning, AsyncVariantThunk}and{TaskReturningILAsync, AsyncVariantImpl}pairsImpact
The new naming convention: enum values prefixed with
Asyncindicate methods using async calling convention (runtime async), whileTaskReturning*indicates methods that return Task/ValueTask types.Modified files:
method.hpp,methodtablebuilder.cpp,class.cpp(12 total changes)Original prompt
This section details on the original issue you should resolve
<issue_title>[Suggestion] Rename AsyncMethodKind.TaskReturning</issue_title>
<issue_description>I got a bit confused by the naming and comments here
Hopefully I understand it correctly now, but if not please correct me. It appears we are using the term "Task Returning" to mean different things in different places:
AsyncMethodKind.TaskReturning - The MethodDesc variant whose entrypoint returns a task and it corresponds to a C# method not marked with the MethodImpl::Async attribute.
// Task-returning methods - Any C# method returning a Task regardless if it has the MethodImpl::Async attribute
I think if you renamed AsyncMethodKind.TaskReturning -> AsyncMethodKind.CompilerAsync it would remove the naming collision and it would also contrast nicely: RuntimeAsync vs CompilerAsync.
</issue_description>
<agent_instructions>Rename AsyncMethodKind::NotAsync to AsyncMethodKind::Ordinary
Rename AsyncMethodKind::RuntimeAsync to AsyncMethodKind::TaskReturningILAsync
Update corresponding comments</agent_instructions>
Comments on the Issue (you are @copilot in this section)
@jakobbotsch > I think if you renamed AsyncMethodKind.TaskReturning -> AsyncMethodKind.CompilerAsync it would remove the naming collision and it would also contrast nicely: RuntimeAsync vs CompilerAsync.Compiler async is quite confusing to me, since in the context of the runtime/VM the only compiler I see is the JIT/interpreter's compiler. But IIUC, here it would refer to something that originally produced the IL that we are seeing? Such a compiler doesn't even need to exist.
Originally these were the names:
https://github.com/dotnet/runtimelab/blame/d95f6eadeeb696071635d9662fbe33f8a0e0d645/src/coreclr/vm/method.hpp#L58-L64
I quite liked those names, but the enum grew and things changed so it wouldn't really map anymore.</comment_new>
<comment_new>@VSadov
No.
AsyncMethodKind.TaskReturningis a method that returnsTask,ValueTaskor generic version of that and the method's definition in metadata is NOT marked asMethodImpl::AsyncBasically, these methoddescs represent ordinary Task-returning methods. The IL may contain a state machine produced by C#/VB/?? or may contain whatever else code.
The IL for these methods is compiled as-is. To participate in runtime async such methods get paired with a thunk that has runtime-async calling convention.
I think
TaskReturningis appropriate as the part that they return tasks is what makes them special, whileNotAsyncare not special at all. </comment_new><comment_new>@VSadov
Perhaps it is
NotAsyncthat is confusing and should be called something else.NotAsyncis for ordinary methods not interesting to runtime async in any way.Ordinarycould probably be a better name, but I think we have other classifications that useOrdinaryso it may be confusing that these are also ordinary but in async sense.</comment_new><comment_new>@noahfalk
Oops I had the right idea in my head but then still wrote the wrong text. I stink. I edited it in the original post so hopefully its correct now.
The key thing that I am suggesting we fix is that "TaskReturning" should have only one definition. I discovered this function which I think really exemplifies it:
The meaning of "TaskReturning" in the method name isn't the same as the meaning of "TaskReturning" in the enum value. Right now AsyncMethodKind::RuntimeAsync is task-returning (IsTaskReturningMethod()==true) and also isn't task-returning (asyncKind != AsyncMethodKind::TaskReturning).
IMO ...
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.