-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Implement {Tcp,Unix}Acceptor::{clone, close_accept} #15704
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
Conversation
Is accepting concurrently an unfortunate side-effect or is it actually a desirable property? If it's not desired, did you consider an alternative design where you can create "Closer" objects that can shut down the acceptor without exposing concurrent acceptance? |
I would classify it akin to reading/writing concurrently like the other I/O primitives. An explicit design decision was made long ago to have these objects be clone-able and then have the |
@alexcrichton I understand the potential use in concurrent reading/writing of other I/O primitives. I don't really understand if there is a benefit to concurrent accepting, and the way this PR is worded, it seemed reasonable to believe that concurrent accepting was the consequence of That said, I don't actually have an objection to concurrent accepting. I just wanted to make sure that a simpler approach wouldn't solve the goal (of terminating an accept loop from another task) without so much support code. But if you'd prefer the consistency, or if there's actually a valid reason to want concurrent accepting, then by all means go ahead. |
Yes I personally prefer the consistency as it's very easy to understand |
// | ||
// To implement close_accept(), we have a self-pipe to ourselves which | ||
// is passed to select() along with the socket being accepted on. The | ||
// self-pipe is never written to unless close_accept() is called. |
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.
Is there a concern here about using up file descriptors, or are you expecting that it's very rare to have a significant number of distinct acceptors?
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 was a bit of a concern that I realized when writing this up, but you're right in that I don't think a large number of acceptors will be too common.
I'm also open to other solutions for this problem, I was just unable to think of any.
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 you can avoid using select()
there are possible solutions, but AFAIK any such solution is platform-specific.
At least on Linux you should use shutdown(SHUT_RD) to cancel accept() rather than an extra pipe fd. On Windows, check whether AcceptEx and CancelIoEx work for this. |
If a task is spinning in an accept loop, there is currently no method of gracefully shutting it down. This PR introduces a way to do so by cloning the acceptor and implementing a close_accept method to unblocking any pending acceptor. As with other I/O methods like this, it is `#[experimental]` from the start and sadly carries with it a good deal of code to support it. Much of the complication is from the fact that you can now concurrently accept on the same socket. I tried to add a good deal of tests for this change, but another set of eyes is always appreciated!
I've had to solve this issue as well, although in my case it was to gracefully stop a task doing interrupt-driven GPIO, rather than accepting TCP connections. I solved it by defining an IoSelector struct, that acts rather like a channel select! statement but for I/O objects, and then selecting between the I/O object served by the task (TCP acceptor in your case, GPIO pins in mine) and another I/O object used to signal that the task should stop gracefully and release I/O resources. On Linux, I implemented this for the native runtime, using the epoll syscall for the IoSelector and eventfd syscalls for the stop signal. On other platforms, IoSelector could be implemented with select (unix) or WaitForMultipleObjects (win32) , and the stop signal with a pipe (unix) or event object (win32). Code here: http://github.com/npryce/rusty-pi |
This commits implements {Tcp,Unix}Acceptor::{clone,close_accept} methods for unix. A windows implementation is coming in a later commit. The clone implementation is based on atomic reference counting (as with all other clones), and the close_accept implementation is based on selecting on a self-pipe which signals that a close has been seen.
This commits implements {Tcp,Unix}Acceptor::{clone,close_accept} methods for all of librustuv. This implementation rewrites much of Access, AccessTimeout, and AcceptTimeout to have type parameter for shared state that all acceptors share (a shared queue of sockets). The incoming/outgoing channels have been removed as all timeouts and such are now managed on the event loop rather than concurrently.
This commit implements TcpAcceptor::{close, close_accept} for windows via WSAEVENT types.
Document the new code for how close_accept and timeouts are implemented.
This commits takes a similar strategy to the previous commit to implement close_accept and clone for the native win32 pipes implementation. Closes rust-lang#15595
088ce1a
to
fd763a5
Compare
If a task is spinning in an accept loop, there is currently no method of gracefully shutting it down. This PR introduces a way to do so by cloning the acceptor and implementing a close_accept method to unblocking any pending acceptor. As with other I/O methods like this, it is `#[experimental]` from the start and sadly carries with it a good deal of code to support it. Much of the complication is from the fact that you can now concurrently accept on the same socket. I tried to add a good deal of tests for this change, but another set of eyes is always appreciated!
If a task is spinning in an accept loop, there is currently no method of gracefully shutting it down. This PR introduces a way to do so by cloning the acceptor and implementing a close_accept method to unblocking any pending acceptor.
As with other I/O methods like this, it is
#[experimental]
from the start and sadly carries with it a good deal of code to support it. Much of the complication is from the fact that you can now concurrently accept on the same socket.I tried to add a good deal of tests for this change, but another set of eyes is always appreciated!