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 does not notify non-ready tasks on drop #846
Comments
Hmm, I'm confused on what's going on under the hood that allows your code to let this happen. Do you have some more code -- the actual Rust/Tokio code -- showing how you're managing to drop the listener from a task other than the task actually using it? |
@tobz We have a global hashmap called a // close(2) is done by dropping the value. Therefore we just need to remove
// the resource from the RESOURCE_TABLE.
pub fn close(&self) {
let mut table = RESOURCE_TABLE.lock().unwrap();
let r = table.remove(&self.rid);
assert!(r.is_some());
}
And there is another struct called There is a method pub fn poll_accept(&mut self) -> Poll<(TcpStream, SocketAddr), Error> {
let mut table = RESOURCE_TABLE.lock().unwrap();
let maybe_repr = table.get_mut(&self.rid); // Use the id of `Resource` to retrieve the actual TcpListener resource
match maybe_repr {
None => panic!("bad rid"),
Some(repr) => match repr {
Repr::TcpListener(ref mut s) => s.poll_accept(), // call poll_accept on TcpListener
_ => panic!("Cannot accept"),
},
}
} With these basic setup, at some random point (after the creation of the listener), a user might call a method called pub fn accept(r: Resource) -> Accept {
Accept {
state: AcceptState::Pending(r), // This `r` happens to be a Resource linking to the target TcpListener
}
} which creates a special struct impl Future for Accept {
type Item = (TcpStream, SocketAddr);
type Error = io::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let (stream, addr) = match self.state {
AcceptState::Pending(ref mut r) => try_ready!(r.poll_accept()), // <- Marked
AcceptState::Empty => panic!("poll Accept after it's done"),
};
match mem::replace(&mut self.state, AcceptState::Empty) {
AcceptState::Pending(_) => Ok((stream, addr).into()),
AcceptState::Empty => panic!("invalid internal state"),
}
}
} So at the marked line inside this Since we are using It is possible that there might be multiple tasks associated with Instead, we hope that a Hope this would be clear... Sorry for any confusing description since I am not that familiar with Tokio internals... |
So, generally speaking, a future will never hold on to more than one task calling it. If you're expecting all tasks that ever poll a single I have a better understanding on your use case here now, though. I think having a |
@tobz Thanks for the explanation. I believe I can enforce single accept at a time myself here, and a |
This is an interesting case and it may be a question of defining guarantees. The task is intentionally not notified on drop. The intent is that, when It sounds like, in your case, you are implementing your own custom "resource" . You will probably want to implement the notification logic you need yourself. In your own Does this make sense? |
I'm going to close the issue, but please let me know if you think this is incorrect. |
Yes - makes sense - thanks for explanations. |
@carllerche Got it. Have implemented something ourselves to track the current task and seems working fine. |
Version
0.1.14
Platform
MacOS High Sierra (but probably also on Linux and Windows)
Description
(Sorry for any confusing description, since I am not very familiar with Tokio and its internals...)
We have a scenario where accept tasks are first put to sleep due to
TcpListener::poll_accept()
returnsAsync::NotReady
, and then we drop theTcpListener
. For some reason, such tasks are not notified on dropping of the listener and thus hangs inNotReady
state forever (instead we want the tasks topoll
on drop so that the tasks could error out)The example scenario is described in denoland/deno#1516 and a current workaround is in denoland/deno#1517 . Currently I have to keep references to the accept tasks and manually call
notify()
on each of the them when the listener is dropped. Not quite sure if this could/should be handled byTcpListener
itself?The text was updated successfully, but these errors were encountered: