-
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
QuicConnection.Dispose should not wait for streams to complete #55156
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsCurrently, there is logic in QuicConnection.Dispose and elsewhere to wait for active streams to complete before closing the msquic handle. In general I don't think we should do this. It's up to the application to decide when a Quic connection is no longer needed and to close it. We cannot and should not try to figure out when the connection is "done". Note we don't do this for CloseAsync, which is what we generally expect a user to do before calling Dispose. So in the expected usage pattern this logic never kicks in. Also, even when the logic does kick in, we are doing an immediate connection close to MsQuic instead of a graceful one. So data in flight will likely end up lost regardless.
|
do you expect msquic to track this? We saw crashed before when close the handle too early. |
Yes, we shouldn't crash in any case and for that we need to keep the connection alive until all it's associated streams are closed. cc @nibanks |
Why? My understanding is that closing the connection will kill all active streams anyway. Right? Edit: If we were/are crashing, we should understand why. My understanding is that msquic handles this properly, so I assume we are doing something wrong on our side. Do we know what it is? Is it the previously identified GCHandle issue? |
I think we need to delay the closing until last stream is gone. I originally did the counting of streams before understanding the msquic internal mechanic. While I think Dispose can free all the managed resources and throw ODE afterwards we sill need to preserve the native handles. |
I still don't understand why we need to do that. Are you saying this is an msquic requirement? If so, what's the specific requirement? Or is it something about how we are using msquic? If so, seems like it would be better to fix how we use msquic. |
Let's @nibanks to comment. I think we should hook to SHUTDOWN_COMPLETED event to free MsQuic resources. AFAIK that can be before or after Dispose is called. |
Agreed... #55044 |
I'm not sure I follow the issue completely here. Is this related to the fact that streams that haven't been started don't get shutdown automatically? FYI, they don't get shutdown complete events because we cannot deliver a notification before you call StreamStart. Perhaps a quick call might help here. |
@wfurt Can you describe the issue you've seen? You understand this better than I do. |
This is something we discussed as well @nibanks. The ultimate question is when it is safe to free all the connection related resources e.g. configuration and the connection itself. Perhaps you can describe the optimal/safe sequence. Prior #52800 we would try to free everything in Dispose and we saw crashes when there were still active streams. #52800 added counting to prevent that. But perhaps there is better way. This is somewhat similar for the streams. Note that c# application may call some functions like Shutdown/Reset even after the final event - I saw tests trying to reset/close on failure and I'm not sure what we want to do there @geoffkizer. We may relay on the SafeHandle and throw ODE but that would be perhaps weird if real .NET Dispose was not called. |
@nibanks Basically, the question here is: (1) Can we call ConnectionShutdown when there are active streams in use. @wfurt is concerned that when we tried to do this in the past, we saw crashes. @wfurt Please add/correct if I missed anything. |
Not sure what you mean here... We should always be relying on the SafeHandle to manage lifetime of the handle. That's what it's for. Used properly, it should prevent use-after-free issues with the handle. |
You can most definitely call |
@nibanks however, is that true that all |
That's correct. |
That means that at least |
Okay, I think I understand the issue now -- thanks all. In the finalization case, we cannot guarantee the ordering of calls to StreamClose and ConnectionClose -- at least not without a bunch of hassle in our finalization logic. @nibanks Is it possible for msquic to allow StreamClose after ConnectionClose? |
It's not currently possible and would not be easy to accomplish. Our entire execution model requires the connection as what manages and drives the queue of work for it and all child streams. StreamClose queues work on the connection; without the connection you can't do the work. |
I'm not saying you need to do any work during StreamClose. You can keep the way that connection teardown works today. It's fine if StreamClose after ConnectionClose is basically a no-op. |
StreamClose has to clean up stuff, and must execute on our worker thread. |
We call |
I'm closing this issue. The original description is incorrect and based on bad assumptions. I believe what we should do is: |
Currently, there is logic in QuicConnection.Dispose and elsewhere to wait for active streams to complete before closing the msquic handle.
In general I don't think we should do this. It's up to the application to decide when a Quic connection is no longer needed and to close it. We cannot and should not try to figure out when the connection is "done".
Note we don't do this for CloseAsync, which is what we generally expect a user to do before calling Dispose. So in the expected usage pattern this logic never kicks in.
Also, even when the logic does kick in, we are doing an immediate connection close to MsQuic instead of a graceful one. So data in flight will likely end up lost regardless.
The text was updated successfully, but these errors were encountered: