erickt / rust-zmq

Rust zeromq bindings.
Apache License 2.0
900 stars 194 forks source link

Get readable() on PollItem (based with a pipe) return false when really readable. #193

Closed ghost closed 7 years ago

ghost commented 7 years ago

I create a PollItem with a pipe RawFD (pipe.0). I write pipe.1 with a byte when SIGINT has been catched, the zmq::poll can return with 1 correctly everytime after the process recv SIGINT. But when I query PollItem's events, I get zero. No readable flag is set.

extern crate nix;
extern crate zmq;

use std::os::unix::io::RawFd;
use std::process;
use nix::sys::signal;

static mut S_FD: RawFd = -1;

extern "C" fn s_signal_handler(_: i32) {
    let rc = unsafe { nix::unistd::write(S_FD, b" ") };
    match rc {
        Ok(_) => {}
        Err(_) => {
            println!("Error while writing to self-pipe.");
            process::exit(1);
        }
    }
}

fn s_catch_signals(fd: RawFd) {
    unsafe {
        S_FD = fd;
    }

    let sig_action = signal::SigAction::new(
        signal::SigHandler::Handler(s_signal_handler),
        signal::SaFlags::empty(),
        signal::SigSet::empty(),
    );
    unsafe {
        signal::sigaction(signal::SIGINT, &sig_action).unwrap();
        signal::sigaction(signal::SIGTERM, &sig_action).unwrap();
    }
}

fn main() {
    let context = zmq::Context::new();
    let socket = context.socket(zmq::REP).unwrap();
    assert!(socket.bind("tcp://*:5555").is_ok());

    let pipefds = nix::unistd::pipe().unwrap();
    nix::fcntl::fcntl(pipefds.0, nix::fcntl::F_GETFL).unwrap();
    nix::fcntl::fcntl(pipefds.0, nix::fcntl::F_SETFL(nix::fcntl::O_NONBLOCK)).unwrap();

    s_catch_signals(pipefds.1);

    let items = &mut [
        zmq::PollItem::from_fd(pipefds.0),
        socket.as_poll_item(zmq::POLLIN),
    ];

    loop {
        let rc = zmq::poll(items, -1);
        match rc {
            Ok(v) => {
                assert!(v >= 0);
                if v == 0 {
                    continue;
                }
                println!("v: {}", v);
                if items[0].is_readable() {
                    println!("pipefds is readable");
                    let buffer = &mut [0; 1];
                    nix::unistd::read(pipefds.0, buffer).unwrap();
                    println!("W: interrupt received, killing server...");
                    break;
                } else {
                    println!("items[0].get_revents: {}", items[0].get_revents());
                }
                if items[1].is_readable() {
                    let buffer = &mut [0; 255];
                    let rc = socket.recv_into(buffer, zmq::DONTWAIT);
                    match rc {
                        Ok(_) => println!("W: recv"),
                        Err(e) => {
                            if e == zmq::Error::EAGAIN || e == zmq::Error::EINTR {
                                continue;
                            }
                            println!("recv: {}", e);
                            process::exit(1);
                        }
                    }
                } else {
                    println!("items[1].get_revents: {}", items[1].get_revents());
                }
            }
            Err(e) => {
                println!("rc err");
                if e == zmq::Error::EINTR {
                    continue;
                }
                println!("zmq::poll: {}", e);
                process::exit(1);
            }
        }
    }
}
rotty commented 7 years ago

I've run your sample code under strace, and I'm pretty sure that the issue is that the PollItem constructed by PollItem::from_fd has its events field initialized to zero, which means it might as well not have been included in the call to zmq::poll. With the current code, there is not even a way to set the events that one is interested in. My plan to address this is:

rotty commented 7 years ago

@zwb-ict Of course, pull requests welcome, if you feel inclined to hack at this right away. I'm off for today.

ghost commented 7 years ago

@rotty I prefer the second plan.

Add a second parameter for specifying the events to from_fd in master (breaking API change, for symmetry with Socket::as_poll_item).

It seems better.

birkenfeld commented 7 years ago

Both are appropriate. It's fine to have a PollItem::set_events in 0.8.x, and for 0.9, since it will break an API that never could have worked - which is a good thing, add the parameter PollItem::from_fd.