-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[QUIC] API QuicStream #71969
[QUIC] API QuicStream #71969
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
Tagging subscribers to this area: @dotnet/ncl Issue DetailsFixes #69675 This PR is based on #71783 so it shows more changes than it actually contains. ATM depends on: microsoft/msquic#2883 but AFAIK only one outerloop test is failing because of this (the one testing stream shutdown due to idle connection). Depending on severity of this and speed on that msquic PR, I may temporarily re-introduce connection state from: runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs Lines 120 to 125 in b480845
Also I'll be filling in comments, not everything is covered. Kept in draft until API is approved.
|
src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
Outdated
Show resolved
Hide resolved
// Concurrent call, this one lost the race. | ||
if (!_receiveTcs.TryGetValueTask(out ValueTask valueTask, this, cancellationToken)) | ||
{ | ||
throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "read")); |
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 is this inside do...while ? One iteration can copy, and the following can throw?
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 loop can iterate at most twice. First, it tries to copy from the buffer, if it succeeds (data are already there) it will break out of the loop in while (... totalCopied == 0);
. If no data are available in the buffer, the await valueTask.ConfigureAwait(false);
will wait on the next RECEIVE
event and once it arrives it will repeat the loop with copying, copy something (or learn about FIN flag) and break out of the loop.
The valueTask
must be awaited each time it's retrieved, even if it's completed and it seems pointless - e.g.: _receiveTcs.TrySetResult();
, since it's backed up by MRVTS which keeps "version" and the await
will reset it (increment it).
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.
A comment like "this will loop at most twice" would be nice :)
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.
Done.
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 still don't understand how we would overcome the race when - between the first and the second iteration - a parallel read could come and "steal" the arrived data? What would happen to the first read? It seems like it could either throw or - would it start waiting on data to arrive again?
|
||
public void Shutdown() => _provider.Shutdown(); | ||
// TODO: memory leak if not disposed | ||
_sendBuffers.Dispose(); |
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.
How do we make sure send buffers are not disposed in the middle of send operation?
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.
By awaiting the valueTask
above which waits on SHUTDOWN_COMPLETE
which is the last event on the stream and means both side are closed, so no send operation is in progress.
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.
Can you add a comment for this please? This is something I am going to forget very soon :D
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.
Added comment 3 lines above, at the valueTask
await.
ac8f5ca
to
ecd1e28
Compare
851251d
to
d1337e9
Compare
src/libraries/Common/tests/System/Net/Http/Http3LoopbackServer.cs
Outdated
Show resolved
Hide resolved
_stream.Shutdown(); | ||
await _stream.ShutdownCompleted().ConfigureAwait(false); | ||
Dispose(); | ||
_stream.CompleteWrites(); |
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.
We no longer do the ShutdownCompleted
because we expect that in Dispose? Is there reason to delay that if we know this is final?
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.
Delay what exactly? DisposeAsync
is controlled from the calling code. In most cases the tests are just doing this:
await using Http3LoopbackStream stream = await connection.AcceptRequestStreamAsync();
await stream.HandleRequestAsync();
And in some cases, the tests are playing with postponing the disposal to later, doing cancellations etc.
Potentially we could replace it with await Task.WhenAll(_stream.WritesClosed, _stream.ReadsClosed);
which should correspond to SHUTDOWN_COMPLETE
give or take. But I haven't experience any issues with the state as-is in this PR, so I don't particularly see reason to change it. After all, this (meaning as-is now) will be usage pattern done by the lib consumers.
src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs
Outdated
Show resolved
Hide resolved
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.
Generally looks ok to me. I wish we have better names for some of the handles but that is ok.
It seems like we also break the build/tests in few places and that should be fixed before merging.
src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/msquic_generated.cs
Outdated
Show resolved
Hide resolved
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.
Looks good modulo existing comments
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
Outdated
Show resolved
Hide resolved
_abortErrorCode = (long)data.ErrorCode; | ||
_state.AbortErrorCode = _abortErrorCode; | ||
_acceptQueue.Writer.TryComplete(ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetConnectionAbortedException(_abortErrorCode))); | ||
_acceptQueue.Writer.TryComplete(ExceptionDispatchInfo.SetCurrentStackTrace(ThrowHelper.GetConnectionAbortedException((long)data.ErrorCode))); |
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.
Do we store the error code anywhere? it might be useful during dump investigation.
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'll stay set in the Exception
in the _acceptQueue
, so it's available, although less conveniently. Do you want me to reintroduce it? Is it something you used while doing investigations?
// Concurrent call, this one lost the race. | ||
if (!_receiveTcs.TryGetValueTask(out ValueTask valueTask, this, cancellationToken)) | ||
{ | ||
throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, "read")); |
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.
A comment like "this will loop at most twice" would be nice :)
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs
Outdated
Show resolved
Hide resolved
|
||
public void Shutdown() => _provider.Shutdown(); | ||
// TODO: memory leak if not disposed | ||
_sendBuffers.Dispose(); |
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.
Can you add a comment for this please? This is something I am going to forget very soon :D
src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
Outdated
Show resolved
Hide resolved
All comments should be answered, only waiting on CI. |
await valueTask.ConfigureAwait(false); | ||
|
||
// This is the last read, finish even despite not copying anything. | ||
if (complete) |
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.
Do I understand correctly, that complete
can only become true in "synchronous" case? Meaning, we didn't actually wait for anything in valueTask above
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.
Yes, but I'd formulate it differently: if complete
was reported as true then the valueTask
will complete synchronously. Note that the valueTask
might have been completed (whether temporarily or finally) before it got to _receiveTcs.TrySetResult(final: true);
.
One failure unrelated: #71233 |
* Quic stream API surface * Fixed test compilation * Fixed http test compilation * HttpLoopbackConnection Dispose -> DisposeAsync * QuicStream implementation * Fixed some tests * Fixed all QUIC and HTTP tests * Fixed exception type for stream closed by connection close * Feedback * Fixed WebSocket.Client test build * Feedback, test fixes * Fixed build on framework and windows * Fixed winhandler test * Swap variable based on order in defining class * Post merge fixes * Feedback and build * Reverted connection state to pass around abort error code * Fixed exception type.
@JamesNK this will need your reaction as well. This contains the stream changes, most interesting will be the ReadsClosed, WritesClosed tasks. |
…72031) (#72106) * [QUIC] API QuicConnection (#71783) * Listener comment; PreviewFeature attribute * Feedback * QuicConnection new API including compilable implementation * Fixed logging * Fixed S.N.Quic and S.N.Http tests * Options now correspond to the issue * Feedback * Comments, PreviewFeature attribute and RemoteCertificate disposal. * Preview feature attribute is assembly wide * Some typos * Fixed test with certificate * Default values as constants * Event handlers split into methods called via switch expression. * Some more comments * Unified unsafe usage * Fixed some more tests * Cleaned up some exceptions and resource strings. * Feedback * Latest greatest API proposal. * Fixed Http solution * Feedback * [QUIC] API QuicStream (#71969) * Quic stream API surface * Fixed test compilation * Fixed http test compilation * HttpLoopbackConnection Dispose -> DisposeAsync * QuicStream implementation * Fixed some tests * Fixed all QUIC and HTTP tests * Fixed exception type for stream closed by connection close * Feedback * Fixed WebSocket.Client test build * Feedback, test fixes * Fixed build on framework and windows * Fixed winhandler test * Swap variable based on order in defining class * Post merge fixes * Feedback and build * Reverted connection state to pass around abort error code * Fixed exception type. * [QUIC] System.Net.Quic API made public (#72031) * System.Net.Quic removed from ASP transport package and made part of SDK ref * Removed manual references to System.Net.Quic.csproj
Fixes #69675
This PR is based on #71783 so it shows more changes than it actually contains, see https://github.com/ManickaP/runtime/compare/mapichov/quic-connection...ManickaP:runtime:mapichov/quic-stream?expand=1 for the actual diff.ATM depends on: microsoft/msquic#2883 but AFAIK only one outerloop test is failing because of this (the one testing stream shutdown due to idle connection). Depending on severity of this and speed on that msquic PR, I may temporarily re-introduce connection state from:
runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
Lines 120 to 125 in b480845
Also I'll be filling in comments, not everything is covered.
Kept in draft until API is approved.