vorner / signal-hook

Rust library allowing to register multiple handlers for the same signal

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example given for signal-hook-mio doesn't seem work

vcm-at-dfinity opened this issue · comments

When I compile the code below, which is almost identical to the example in the docs (except that I'm interested in handling SIGINT), and then send a SIGINT to the process running it I see:

thread 'main' panicked at 'Can't poll: Os { code: 4, kind: Interrupted, message: "Interrupted system call" }', src/main.rs:22:38

Which means that the poll got interrupted instead of letting me handle the signal. What have I done wrong?

Thanks

use std::io;
use mio::{Events, Interest, Poll, Token};
use signal_hook::consts::signal::SIGINT;
use signal_hook_mio::v0_7::Signals;

const SIGNAL: Token = Token(10);

fn main() -> io::Result<()> {
    let mut poll = Poll::new()?;
    let mut signals = Signals::new(&[SIGINT])?;
    let signal_token = Token(0);
    poll.registry().register(&mut signals, signal_token, Interest::READABLE)?;
    let mut events = Events::with_capacity(10);

    loop {
        poll.poll(&mut events, None).expect("Can't poll");

        for event in events.iter() {
            match event.token() {
                SIGNAL => {
                    for sig in signals.pending() {
                        println!("Got signal {}", sig);
                    }
                    return Ok(());
                }
                _ => println!("Got unexpected event: {:?}", event),
            }
        }
    }
}

This is my cargo dependencies:

mio = { version = "0.7", features = ["os-poll", "os-ext", "pipe"] }
signal-hook-mio = { version = "0.2.0", features = ["support-v0_7"] }
signal-hook = { version = "0.3.6", features = ["iterator"] }

Hello

Little bit of background first. Most syscalls return an error if any signal is delivered to the application before they had a chance to do anything. The EINTR („Interrupted“) is the particular error. The signal is still delivered as usual and the syscall can be safely retried, it exists mostly so old applications had a chance to wake up from blocking calls, do something about a signal and then continue on their way. Nowadays, most applications simply make that EINTR non-fatal and retry.

To make your example work, simply check what error arrived and don't abort on that particular kind, just keep going ‒ the signal will get delivered in another round (it gets delivered, written into an internal pipe, but the EINTR happens sooner than the other end becomes readable).

I'll leave this open for myself to improve that example. It was mostly just a copy + modification of the other asynchronous examples around, but the others handle EINTR under the hood automatically.

Thanks for the explanation! I'll try. This makes sense and this is information that could be very useful on the documentation for signal-hook-mio.

In fact, I'm working on a larger application which was handling signals in what seemed like a non-deterministic fashion. Now I understand I must continue on any syscall that returns EINTR instead of panicking out. :)

edit: I started handling EINTR in all system calls on my code and signal handling now works like a charm. Thanks @vorner !

This makes sense and this is information that could be very useful on the documentation for signal-hook-mio.

I've updated the example. Apart from that, I don't think the signal-hook-mio is the right place to document this. The crate is just an implementation of a specific design pattern/signal handling trick, but the EINTR is general platform behavior. It's like knowing that one can't open the same file multiple times on Windows and trying to document that with a image handling library. If anywhere, it should be at the mio's poll itself (after all, the error doesn't even come out from anything inside signal-hook-mio).