-
Notifications
You must be signed in to change notification settings - Fork 206
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
Prototyping T RuntimeHelpers.Await<T>(Task<T>)
#2941
base: feature/async2-experiment
Are you sure you want to change the base?
Conversation
@@ -208,6 +208,75 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter | |||
SuspendAsync2(sentinelContinuation); | |||
return; | |||
} | |||
|
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.
The interesting part. The rest of code changes are mechanical - to make the helper known as special method.
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 don't see a good reason to not write this function in terms of UnsafeAwaitAwaiterFromRuntimeAsync
. This looks more complicated than necessary.
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 don't see a good reason to not write this function in terms of UnsafeAwaitAwaiterFromRuntimeAsync. This looks more complicated than necessary.
Possibly. I started on that path, but was running into asserts (something about conditional BB not ending with conditional jump,...).
I was not sure if that was something that I did wrong or issues with SuspendAsync2
in unusual context.
This way worked though.
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 don't think we will need an explicit SuspendAsync2
once we switch to use UnsafeAwaitAwaiter...
with everything hooked up. The JIT should create the state machine itself at that point.
// // RETURN {task.Result, null} | ||
// // | ||
// T result = task.Result; | ||
// ReturnAsync2(Unsafe.AsPointer(ref result)); |
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.
Just guessing how ReturnAsync2
could look.
If generic intrisic is ok, it could also be just ReturnAsync2<T>(result)
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 don't think we need an intrinsic, this can just be a normal return.
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 a regular return guarantee that the continuation return is null?
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.
It would need the VM to tell the JIT this function has runtime-async calling convention. That's probably something we'd want anyway. It might also be the only thing we need to allow using UnsafeAwaitAwaiterFromRuntimeAsync
in its implementation.
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 will try. We do not do that for UnsafeAwaitAwaiterFromRuntimeAsync
, but that may work just because it is a simpler method.
return await Loop(); | ||
#endif | ||
|
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.
Using Await
in a benchmark/test.
} | ||
|
||
internal static class AwaitHelper<T> | ||
{ |
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.
Resuming the continuation simply pushes the result into the caller continuation. (or throws, if faulted)
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 wonder if UnsafeAwaitAwaiterFromRuntimeAsync
needs to call GetResult()
upon resuming as well? (in case it faulted)
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.
Also GetAwaiter().GetResult()
seems more appropriate than .Result
. This is just a rough prototype anyways.
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 wonder if
UnsafeAwaitAwaiterFromRuntimeAsync
needs to callGetResult()
upon resuming as well? (in case it faulted)
The caller is responsible for calling awaiter.GetResult()
(UnsafeAwaitAwaiterFromRuntimeAsync
has no way to bind to the awaiter anyway).
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.
The caller is responsible for calling awaiter.GetResult()
Ah, right, in this case we have code in the caller.
switched to state machine version of Await. It was a bit more involved than I expected. The main issue is that for the "magic" methods you can't figure their "async-ness" by only looking at the signature, which is kind of ByDesign. I think we may have to keep the special methods as Intrinsics. Explicit async is also a bit viral - once you declare something as async machine you'd need to call with async call convention and vice versa, lest you get into obscure crashes from dereferencing continuations that are not actually passed by the caller or calee and depend on what junk there was in the corresponding register. I like the new approach more, though. It seems more generally useful/scalable in case we need more "magic" helpers. |
Not for merging, just taking a look how
RuntimeHelpers.Await
could be implemented.The code actually runs. The implementation is suboptimal because we always suspend.
We do not have to, but it would require implementing
ReturnAsync2
intrinsic, the value-returning counterpart ofSuspendAsync2
.It seems doable, but needs a bit more JIT expertise.
Even with always supending the perf is not too bad - roughly 30% worse on the
varying-yield
benchmark.Although this benchmark suspends a lot, thus is not impacted as much by unconditional suspend.
For the real implementation we would definitely need
ReturnAsync2