Skip to content

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

Merged
merged 5 commits into from
Aug 25, 2014

Conversation

alexcrichton
Copy link
Member

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!

@lilyball
Copy link
Contributor

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?

@alexcrichton
Copy link
Member Author

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 close_foo methods instead of any of a number of other strategies. We could create separate closer objects, but I prefer consistency across all objects for now.

@lilyball
Copy link
Contributor

@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 cloneing but was not actually desired.

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.

@alexcrichton
Copy link
Member Author

Yes I personally prefer the consistency as it's very easy to understand clone as it's so prevalent everywhere else. The only real painful part of concurrent accepts was librustuv, but it ended up being nicely abstracted so it doesn't matter a whole lot.

//
// 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.
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

@bill-myers
Copy link
Contributor

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.

bors added a commit that referenced this pull request Aug 7, 2014
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!
@npryce
Copy link

npryce commented Aug 10, 2014

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
bors added a commit that referenced this pull request Aug 25, 2014
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!
@bors bors merged commit fd763a5 into rust-lang:master Aug 25, 2014
@alexcrichton alexcrichton deleted the issue-15595 branch August 25, 2014 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants