-
-
Notifications
You must be signed in to change notification settings - Fork 402
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
end endpoint driver on manually close #1828
end endpoint driver on manually close #1828
Conversation
I don't think this is harmful, but I'm not sure it's useful either. What's the motivation? If you're trying to make the socket available for reuse, this still won't be reliable, because it takes some time for connections to close (i.e. for |
Motivation is try to mitigate the fact that calling close is a bit of a "fire and forget" operation as an user. There is no way to know for sure when graceful shutdown operations are complete. So this tries to speed it up |
That's
How does a possibly earlier, but still unpredictable, drop help? What's the use case that this is addressing? |
I see this is hard to see from quinn's point of view. Let me try to explain a bit better, at least from my -potentially poor- understanding of what's going on underneath.
This means there is no way to actively wait for the resources held be socket to be freed. To put it very specifically, as far as I can tell there is no async This is observable from an implementation of Sorry for the convoluted explanation, but I hope this helps |
Thanks, those additional details are helpful. Putting it in my own words, you have a custom Unfortunately, this change will not address your use case. Every Consider |
Correct, thanks for bearing with me
Thanks for pointing out that dropping the socket won't happen even in this scenario.
Since the objective is to free resources rebind is not really what I'm looking for. Informing the socket of closing on a side channel is I think the best shot, but without this change there is still an undesirable observable behaviour. Maybe our Consider an struct MySocket<I: AsyncUdpSocket>(Arc<Mutex<Option<I>>>);
struct Super<I: AsyncUdpSocket> {
socket: MySocket<I>,
endpoint: quinn::Endpoint,
} with a and a self.endpoint.close(code, reason);
self.endpoint.wait_idle().await;
self.socket.expensive_close().await;
// underlying socket is already dropped, and it was dropped after idle since the socket is still being polled after close, the returned io error from Beside this being annoying, it feels wrong. After close and idle the endpoint won't be able to send or receive so it being polled is something I think should not be happening - unless we are misunderstanding the semantics of closing the endpoint and its connections |
Specifically rebinding with a dummy socket (e.g. a ZST that stubs all trait methods and owns no resources) would serve your needs.
An easy solution is to silently no-op rather than returning an error.
QUIC endpoints may perform I/O even when there are no connections. For example, I/O is required to reject incoming connection attempts gracefully. It's not obvious that |
as far as I can tell the only rebinding option is with a "real" socket. https://docs.rs/quinn/0.10.2/quinn/struct.Endpoint.html#method.rebind |
Good point. We should probably expose a lower-level version, since |
See #1831. |
IIUC the change as proposed might result in the As far as I understand what we are really after with the custom AsyncUdpSocket shutdown is knowing when Quinn is done with the socket. Which currently we only know when it is dropped. The thing that seems to be missing is that an application can never really know when Quinn is done. It spawns various tasks in the background which hold on to some resources. IIUC this problem would also be solved by knowing when an endpoint has released all it's resources, i.e. all the tasks are done. Maybe this could be in the guise of some sort of let tasks = self.endpoint.take_task_set();
self.endpoint.close(code, reason);
tasks.await;
self.socket.expensive_close().await; |
That's already provided by the resources in question getting @divagant-martian is #1831 an adequate solution for your use case? |
This is true for someone who implements an AsyncUdpSocket, but that's a lot to ask for a normal library users. They would still benefit from knowing when all resources are freed. I realise that's not where this PR started from, but this knowing when all resources are released are at the root of the reason this PR was started. Also, relying on drop is not that simple. It is only possible if everything relies on drop. n0-computer/iroh#2227 (comment) describe why doing so when you need to support an explicit So I still think there might be some benefit to have a mechanism to know when Quinn is done with all it's resources. |
What is the concrete use case? |
The obvious answer is that the above use case of having to close the sockets would have been trivially simple if something like this would have existed. I consider the current solution of rebinding with a stubbed-out socket rather a weird hack. Another use case is for tests where ideally, at least for successful tests, the resources would be cleaned up which helps when running a large test suite in parallel. Though in general I'm rather uncomfortable if a library does not allow closing its resources entirely. Sooner or later this results in a race-condition where something is trying to wait for things to finish but then they aren't. |
Maybe it's time for iroh to drop quinn and drop down to the quinn-proto level? |
When calling
quinn::Endpoint::close
the socket resources are not freed until all references to the endpoint are dropped. This makes it so that the socket is freed on close.Any suggestions on the best way to test this are welcome