-
Notifications
You must be signed in to change notification settings - Fork 10k
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
Stop doing sync over async in Produce100Continue #31650
Conversation
TryStart(); | ||
TryStartAsync(); |
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.
Isn't this a behavior change to go from blocking to fire-and-forget? Can TryStartAsync throw asynchronously like if the client has disconnected?
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 Write100ContinueAsync()
could realistically return an incomplete ValueTask this is a change in behavior. Not sure how that could ever happen though. If it could happen, not blocking is waaay better than block because at least you return the thread to the pool. TryStartAsync should never be able to throw asynchronously because it's a write without a canceled token.
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.
Add an assert. I dislike the fire and forget as well unless we have an explicit check that it's always completed.
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.
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.
Whoops, plus a10c9d9
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 love that bug 😄
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 dislike the fire and forget
I totally agree. I also dislike fire-and-forget with a passion, but what's even worse is blocking on I/O. This isn't the only place we don't await output-writing ValueTask
s we expect to be usually completed.
Now unlike with HTTP/2 where you could conceive of a scenario where output backpressure has built up prior to sending a WINDOW_UPDATE
or RST_STREAM
, it's hard to come up with one for an HTTP/1.1 "100 continue" response. That said, it's not a mathematical invariant. I could write a unit test with a custom transport and write buffering disabled to force it. For that reason, I don't think we should add this Debug.Assert like we don't for WINDOW_UPDATE
or RST_STREAM
. If we think it's worthwhile we can store the task and await it in ConsumeAsync(), but I don't think that's 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.
That makes sense to me - if it matches what we do for RST_STREAM
and WINDOW_UPDATE
seems like we can remove the assert here
src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs
Outdated
Show resolved
Hide resolved
Got what looks to be a flaky failure in a selenium test, Other than that, any other concerns @Tratcher @davidfowl?
|
src/Servers/Kestrel/Core/src/Internal/Http/Http1ChunkedEncodingMessageBody.cs
Show resolved
Hide resolved
Task readTask = OnReadStartedAsync(); | ||
if (!readTask.IsCompletedSuccessfully) | ||
{ | ||
return readTask; | ||
} | ||
|
||
return Task.CompletedTask; |
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 not:
Task readTask = OnReadStartedAsync(); | |
if (!readTask.IsCompletedSuccessfully) | |
{ | |
return readTask; | |
} | |
return Task.CompletedTask; | |
return OnReadStartedAsync(); |
If the OnReadStartedAsync task is complete then return just return it. And if it is not complete then also just return it...
@@ -183,7 +202,11 @@ protected ValueTask<ReadResult> StartTimingReadAsync(ValueTask<ReadResult> readA | |||
{ | |||
if (!readAwaitable.IsCompleted) | |||
{ | |||
TryProduceContinue(); | |||
ValueTask<FlushResult> continueTask = TryProduceContinueAsync(); |
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.
Shouldn't we indicate that the ValueTask result has been consumed? @stephentoub submitted a PR to do that for historical code: #31221
if (!continueTask.IsCompletedSuccessfully)
{
return StartTimingReadAwaited(continueTask, readAwaitable, cancellationToken);
}
else
{
continueTask.GetAwaiter().GetResult();
}
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.
Hmm. I'd have expected the code in the PR to trigger CA2012, providing the same feedback as James. Were no warnings issued for this?
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.
Your .editconfig changes are still there: https://github.com/dotnet/aspnetcore/blob/main/.editorconfig
I'm not familiar with configuring analyzers in editconfigs or CA2021. I'm not sure why it wasn't prompted.
src/Servers/Kestrel/Core/src/Internal/Http2/Http2MessageBody.cs
Outdated
Show resolved
Hide resolved
Got a real test failure in
Looking |
I think it isn't you: #31743 (comment) |
@JamesNK should we just disable in this PR? |
Issue: #31777 Add skip or quarantine back in this PR. |
Done |
@JamesNK any other concerns? |
Fixes #31225
Draft with a first-pass at fixing the issue.