-
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?
Changes from 1 commit
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 |
---|---|---|
|
@@ -208,6 +208,75 @@ public static void UnsafeAwaitAwaiterFromRuntimeAsync<TAwaiter>(TAwaiter awaiter | |
SuspendAsync2(sentinelContinuation); | ||
return; | ||
} | ||
|
||
// TODO: should this be called "AwaitFromRuntimeAsync" ? (i.e. same as above, but no "Awaiter") | ||
// | ||
// Marked intrinsic since this needs to be | ||
// recognizes as an async2 call. | ||
[Intrinsic] | ||
[BypassReadyToRun] | ||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
public static unsafe T Await<T>(Task<T> task) | ||
{ | ||
// TODO: handle complete tasks more efficiently. | ||
//if (!task.IsCompleted) | ||
//{ | ||
ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; | ||
Continuation? sentinelContinuation = state.SentinelContinuation; | ||
if (sentinelContinuation == null) | ||
state.SentinelContinuation = sentinelContinuation = new Continuation(); | ||
|
||
Continuation myContinuation = new Continuation(); | ||
myContinuation.GCData = new object[] { task }; | ||
myContinuation.Resume = &AwaitHelper<T>.Resume; | ||
|
||
state.Notifier = task.GetAwaiter(); | ||
sentinelContinuation.Next = myContinuation; | ||
|
||
// RETURN {default(T), myContinuation} | ||
// | ||
SuspendAsync2(myContinuation); | ||
|
||
// unreachable | ||
return task.ResultOnSuccess; | ||
//} | ||
//else | ||
//{ | ||
// // 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 commentThe reason will be displayed to describe this comment to others. Learn more. Just guessing how If generic intrisic is ok, it could also be just 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. 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 commentThe 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 commentThe 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 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. I will try. We do not do that for |
||
// | ||
// // unreachable | ||
// return result; | ||
//} | ||
} | ||
|
||
internal static class AwaitHelper<T> | ||
{ | ||
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. 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 commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if 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. Also 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.
The caller is responsible for calling 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.
Ah, right, in this case we have code in the caller. |
||
public static Continuation? Resume(Continuation continuation) | ||
{ | ||
Task<T> task = (Task<T>)continuation.GCData![0]!; | ||
Continuation next = continuation.Next!; | ||
|
||
if (IsReferenceOrContainsReferences<T>()) | ||
{ | ||
next.GCData![0] = task.Result; | ||
} | ||
else | ||
{ | ||
int retIndex = | ||
(next.Flags & CorInfoContinuationFlags.CORINFO_CONTINUATION_OSR_IL_OFFSET_IN_DATA) != 0 ? | ||
4 : | ||
0; | ||
|
||
// TODO: WriteUnaligned? | ||
Unsafe.As<byte, T>(ref next.Data![retIndex]) = task.Result; | ||
} | ||
|
||
return null; | ||
} | ||
} | ||
#endif | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.IL"> | ||
<PropertyGroup> | ||
<Optimize>True</Optimize> | ||
<DefineConstants>AWAIT;$(DefineConstants)</DefineConstants> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="varying-yields.cs" /> | ||
</ItemGroup> | ||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
//#define ASYNC1_TASK | ||
//#define ASYNC1_VALUETASK | ||
|
||
#pragma warning disable 4014, 1998 | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
|
@@ -91,9 +93,19 @@ async2 Task<long> | |
double liveState3 = _yieldProbability; | ||
|
||
if (depth == 0) | ||
#if AWAIT | ||
return RuntimeHelpers.Await(Loop()); | ||
#else | ||
return await Loop(); | ||
#endif | ||
|
||
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. Using |
||
long result = | ||
#if AWAIT | ||
RuntimeHelpers.Await(Run(depth - 1)); | ||
#else | ||
await Run(depth - 1); | ||
#endif | ||
|
||
long result = await Run(depth - 1); | ||
Sink = (int)liveState1 + (int)liveState2 + (int)(1 / liveState3) + depth; | ||
return result; | ||
} | ||
|
@@ -117,7 +129,12 @@ async2 Task<long> | |
{ | ||
for (int i = 0; i < 20; i++) | ||
{ | ||
numIters += await DoYields(); | ||
numIters += | ||
#if AWAIT | ||
RuntimeHelpers.Await(DoYields()); | ||
#else | ||
await DoYields(); | ||
#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.
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.
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 useUnsafeAwaitAwaiter...
with everything hooked up. The JIT should create the state machine itself at that point.