3

I am a Rust beginner struggling with a problem of async IO. I've decided to use mio.

I've read some source code + tutorials but there is still some fundamental part that I do not understand. I am setting up a server with netcat -k -l 127.0.0.1 9999. Then I am running simple test with cargo (code below). I was expecting to see panic with "ready" or "tick". But it never happens and test is running forever.

extern crate mio;
use mio::*;
#[allow(unused_imports)]
use mio::tcp::TcpStream;

#[allow(dead_code)]
struct MyHandler;

impl Handler for MyHandler {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, _event_loop: &mut EventLoop<Self>, _token: Token, _event_set: EventSet) {
        panic!("ready");
    }

    fn tick(&mut self, _event_loop: &mut EventLoop<Self>) {
        panic!("tick");
    }
}

#[test]
fn mio_test1() {
    let addr = "127.0.0.1:9999".parse().unwrap();
    let mut event_loop = EventLoop::<MyHandler>::new().unwrap();
    event_loop.register(&TcpStream::connect(&addr).unwrap(), Token(0), EventSet::readable(), PollOpt::level()).unwrap();
    event_loop.run(&mut MyHandler).unwrap();
}
Maciej Donajski
  • 298
  • 2
  • 13
  • 3
    Using `#[allow(...)]` is overkill for this situation. It would be better to simply [prepend the unused variables with an underscore](http://is.gd/8Nl0rE). Related [question](http://stackoverflow.com/a/32751177/155423). – Shepmaster Feb 18 '16 at 18:12
  • Thanks @Shepmaster for your help. I've seen your edit with reregister and I've fixed that to be register and it was causing the OS error. And now I am back with original question again. I replaced println! with panic! to be sure that the handlers are not executed. – Maciej Donajski Feb 18 '16 at 18:38

1 Answers1

2

Your problem arises from the fact that the socket you open is closed before your event loop has the chance to run.

You current code is roughly equivalent to this:

let addr = "127.0.0.1:9999".parse().unwrap();
let mut event_loop = EventLoop::<MyHandler>::new().unwrap();
{
    let sock = TcpStream::connect(&addr).unwrap();
    event_loop.register(&sock, Token(0), EventSet::readable(), PollOpt::level()).unwrap();
}   // The socket is closed here, before the event loop runs
event_loop.run(&mut MyHandler).unwrap();

So the fix is just to bind the socket to a variable so it will stay open when you call the event loop.

let addr = "127.0.0.1:9999".parse().unwrap();
let mut event_loop = EventLoop::<MyHandler>::new().unwrap();
let sock = TcpStream::connect(&addr).unwrap();
event_loop.register(&sock, Token(0), EventSet::readable(), PollOpt::level()).unwrap();
event_loop.run(&mut MyHandler).unwrap();

Your code then behaves as you expect, and panics as soon as there is something to read on the socket.

Vaelden
  • 2,089
  • 1
  • 12
  • 14
  • It is a good answer. Do I understand correctly that I borrowed socket to register method and when it returns it is dropped? Is it true that if event_loop instance would set the socket as a member it would not be dropped? – Maciej Donajski Feb 19 '16 at 11:18
  • Your code effectively creates a socket, passes a reference to it to the register function, and then drops the socket at the end of the statement as it is not bound to any variable. If by "set the socket as a member" you mean if register took ownership of the socket and stored it, then yes the socket would be kept open and your code would work. – Vaelden Feb 19 '16 at 12:55
  • This doesn't make any sense to me. The *whole point* of lifetimes is to prevent this kind of thing from happening! If the event stream keeps a reference to the socket, then the compiler should complain that it doesn't last long enough! – Shepmaster Feb 19 '16 at 15:26
  • Well technically the event loop doesn't keep a reference to the socket. It is actually just getting the file descriptor, pass it to the kernel APIs along with the Token and then discards it. So in the end it is the kernel that does all the bookkeeping. – Vaelden Feb 19 '16 at 18:39