-
Notifications
You must be signed in to change notification settings - Fork 723
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
Properly handle generic ValueTask await adapter #4640
Conversation
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 for doing this.
Looks good.
@RenderMichael Any idea why the tests after 3 hours are still running? |
Ah never mind, I was completely misinterpreting those results. I guess the failure was at an unfortunate location where it caused an infinite wait instead of an exception bubbling up. |
Good there are tests then! @RenderMichael Btw, are there enough tests to cover the changes done here ? |
@OsirisTerje I’m happy to add a unit test here, note that while there’s no explicit test coverage, as you can see from the CI failures, this was implicitly tested by the rest of the suite. |
@jnm2 wrote the AsyncAdapters back in 2018, and he added a series of tests, AsyncExecutionApiAdapterTests, AwaitableReturnTypeTests, and more - search for "await" in the test explorer. @jnm2 might explain more here, but I got a feeling we should have added more tests here when ValueTask was introduced/changed. And the old #2774 is still open. @RenderMichael Nice if you can have a look at the tests I mention above, and see if you could add to those. The issues related to ValueTask are old, so this is not new, but when we start changing things, that's when I at least start to be a bit worried :-) About implicitly tests, I am not sure if that was implicit or a side-effect, if it was implicit, it should have shown up in the coverage. UPDATE: I tested on the old code, not your PR, so this is not relevant :-) Sorry. @stevenaw @manfred-brands Comments ? |
@RenderMichael Can you share the code you use to verify that it is working. I am trying with the repro code for the #4588 issue and can't get it to go into the ValueTask awaiter. |
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! LGTM :-)
@OsirisTerje I don't think there's a test we can write which will fail without this PR, since the C# pattern-based adapter catches this scenario anyways, so I had to use to the debugger to watch the flow. The tests I added passed even before this PR, but they hit the code paths (and the other tests hit the negative code paths which caused an exception previously). |
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 for noticing and fixing this @RenderMichael ! LGTM
I apologize for missing #4543. What is the reason that we don't use CSharpPatternBasedAwaitAdapter for ValueTask? |
Benchmark codeusing BenchmarkDotNet.Attributes;
using NUnit.Framework.Internal;
using NUnit.TestData;
namespace NUnit.Framework;
[MemoryDiagnoser(false)]
public class AwaitableBenchmarks
{
public IEnumerable<object> ValueData()
{
yield return Task.Yield();
yield return ValueTask.FromResult(30);
yield return ValueTask.FromException<object>(new Exception());
yield return new AwaitableReturnTypeFixture.CustomAwaitable();
yield return new AwaitableReturnTypeFixture.CustomAwaitableWithImplicitOnCompleted();
yield return new AwaitableReturnTypeFixture.CustomAwaitableWithImplicitUnsafeOnCompleted();
}
[Benchmark]
[ArgumentsSource(nameof(ValueData))]
public AwaitAdapter? After(object maybeTask)
{
var awaiter = ValueTaskAwaitAdapter.TryCreate(maybeTask)
?? CSharpPatternBasedAwaitAdapter.TryCreate(maybeTask)
?? FSharpAsyncAwaitAdapter.TryCreate(maybeTask);
if (awaiter is not null)
{
return awaiter;
}
throw new InvalidOperationException();
}
[Benchmark(Baseline = true)]
[ArgumentsSource(nameof(ValueData))]
public AwaitAdapter? Before(object maybeTask)
{
var awaiter = CSharpPatternBasedAwaitAdapter.TryCreate(maybeTask)
?? FSharpAsyncAwaitAdapter.TryCreate(maybeTask);
if (awaiter is not null)
{
return awaiter;
}
throw new InvalidOperationException();
}
}
I'm not versed enough in BenchmarkDotNet to give a better parameter name than the default I can't speak for why it was initially implemented, but it noticeably speeds up the ValueTask<> check (in exchange for higher allocations) without meaningfully regressing the other cases. |
Fixes #4639