-
Notifications
You must be signed in to change notification settings - Fork 60
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
TcpListener: drain all pending accepts #224
Conversation
Good catch! Could you add a test? |
139f6a8
to
652ed46
Compare
For sure, added one now! |
652ed46
to
0841c40
Compare
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 pretty good! Let me know what you think about the accept simplification.
src/net/tcp/listener.rs
Outdated
let host = world.current_host_mut(); | ||
let (syn, origin) = host.tcp.accept(self.local_addr)?; | ||
let (syn, origin) = host.tcp.accept(self.local_addr).ok_or(Retry::Wait)?; |
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.
Could we return from the World::current
closure here and avoid needing the enum? accept -> None signals the wait, syn.ack.send() -> err signals the retry, and the Ok path remains the same.
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.
Just to check my understanding: You are suggesting that we shorten the World::current
closure to already end here and then do the syn.ack.send()
and testing of the result outside of it, where we can continue
the loop if necessary?
That would work, but in the Ok
case we're have to enter World::current
again to get access to the host
back, unless I'm missing something. Still sounds a bit simpler, I'll give it a try!
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.
Pushed a change that gets rid of the enum and breaks out of the loop once we know we don't need to retry anymore. Lmk what you think!
This commit makes `TcpListener::accept` try to accept the next connection without waiting for a notify, if the previous connection couldn't be established because the peer already went away. The change is meant to fix a bug where a pending connection sometimes wouldn't be accepted. Consider this series of events: * Server runs `TcpListener::accept`. There are no connections pending, so it starts waiting on the `notify`. * Client 1 sends a SYN, which gets pushed to `ServerSocket::deque` and `ServerSocket::notify` is notified. * Client 2 sends a SYN, which gets pushed to `ServerSocket::deque` and `ServerSocket::notify` is notified. * Client 1 goes away. * Server wakes up because the `notify` was notified. * Server tries to accept the connection from client 1, which fails because client 1 went away. * Server starts waiting on the `notify` again. We end up in a situation where the connection from client 2 is still pending but the server doesn't accept it because it's waiting for a notification that was already sent.
0841c40
to
5926783
Compare
This PR makes
TcpListener::accept
try to accept the next connection without waiting for a notify, if the previous connection couldn't be established because the peer already went away.The change is meant to fix a bug where a pending connection sometimes wouldn't be accepted. Consider this series of events:
TcpListener::accept
. There are no connections pending, so it starts waiting on thenotify
.ServerSocket::deque
andServerSocket::notify
is notified.ServerSocket::deque
andServerSocket::notify
is notified.notify
was notified.notify
again.We end up in a situation where the connection from client 2 is still pending but the server doesn't accept it because it's waiting for a notification that was already sent.