-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Add AsyncValueTaskMethodBuilder in support of C# feature #10201
Conversation
LGTM |
3c3b360
to
509b977
Compare
Thanks, Lucian. Anyone else want to review before I merge this? |
@@ -4,7 +4,7 @@ | |||
<PropertyGroup> | |||
<ProjectGuid>{F24D3391-2928-4E83-AADE-B34423498750}</ProjectGuid> | |||
<AssemblyName>System.Threading.Tasks.Extensions</AssemblyName> | |||
<AssemblyVersion>4.0.1.0</AssemblyVersion> | |||
<AssemblyVersion>4.1.0.0</AssemblyVersion> |
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.
Why this vs. 4.0.2?
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've been told that adding APIs requires bumping the minor version. @weshaggard, is that correct?
LGTM |
public void Create_ReturnsDefaultInstance() | ||
{ | ||
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder(); | ||
Assert.Equal(default(AsyncValueTaskMethodBuilder<int>), b); |
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.
This was a bit confusing to me at first, because AsyncValueTaskMethodBuilder.Create
does assign to its _methodBuilder
field. But it turns out that it assigns it to default
anyways (or technically calls a method returning default
and assigns that). I guess we would need to change this test if that ended up returning something else? (There's a comment suggesting we might here)
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 guess we would need to change this test if that ended up returning something else?
Yup
LGTM |
The C# compiler just merged a feature to allow arbitrary Task-like types in the return position of async methods. In support of that, this commit adds the necessary builder to allow ```ValueTask<T>``` to be used in this way.
509b977
to
0458fc9
Compare
// ExecutionContext as we don't have access to any of the relevant methods. We also can't | ||
// call _methodBuilder.Start, as then we'll always end up creating a Task<T>. At the moment the | ||
// only solution is to skip the ExecutionContext barrier that we'd want to put in place. | ||
stateMachine.MoveNext(); |
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.
@ericeil, not to block this PR, but for subsequently, any thoughts about what to do here?
Outside of mscorlib, the options are relatively limited. If we were to bump up to the latest .NET Standard version, we could use ExecutionContext.Capture/Run to simulate what's being done internally in AsyncTaskMethodBuilder.Start, but that seems unfortunately expensive, at least for desktop, and it means either we don't support earlier standards, or we have multiple builds and end up with different behaviors based on which standard you're targeting.
I'm tempted to leave this as a wart for now. The impact as you know will be that any ExecutionContext-related changes that occur in the async method prior to it yielding will be visible to the caller, but none of the options seem particularly good.
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.
Could you just replace the call to MoveNext
with this?
AsyncTaskMethodBuilder.Create().Start(ref stateMachine);
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.
At least in the CoreCLR implementation, that looks like it would do the trick without allocating unnecessarily.
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.
Could you just replace the call to MoveNext with this?
I'd convinced myself that the answer was "no", that doing so would cause it to access the AsyncTaskMethodBuilder's Task and force the allocation that all of this is trying to avoid. But looking at it again, Start's whole purpose is really just to wrap the stateMachine's invocation exactly the way we want to, so it should be fine. I'll verify, but... I think this works :)
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.
Thanks, Eric! Fixed.
Thanks, all. |
/// <summary>true if <see cref="_result"/> contains the synchronous result for the async method; otherwise, false.</summary> | ||
private bool _haveResult; | ||
/// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary> | ||
private bool _useBuilder; |
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 curious, maybe these two bools could be aggregated into an int
instead? If I remember correctly, bools actually take up 4 bytes since the CLR pads them to align field accesses. Maybe it could be changed to a single field (byte
, short
, or int
) that uses flags.
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.
Making these bools into an int will make it worse if the TResult is two bytes or less; on 32-bit, the TResult and the two bools can then be placed in the same word. Other than that, on both 32-bit and 64-bit, size-wise it's the same, and using an int would just make the code more complicated.
The C# compiler just merged a feature to allow arbitrary Task-like types in the return position of async methods. In support of that, this commit adds the necessary builder to allow
ValueTask<T>
to be used in this way.I've not yet tried this with compiler bits.
cc: @cston, @ljw1004, @mellinoe, @terrajobst, @jaredpar
Related to dotnet/roslyn#12518