Search code examples
rustsignalslibc

`libc::signal` for `impl` method


I would like to call an impl method on sending a SIGUSR1 signal.

Consider the following example:

use libc::SIGUSR1;
use std::{thread, time};

struct Foo {
}

impl Foo {
    fn show(&self) {
        println!("Foo SIGNAL")
    }
}

fn main() {
    let foo = Foo {};

    unsafe {
        libc::signal(SIGUSR1, foo.show as usize);
    }

    loop{
        println!("sleeping for 1 sec");
        thread::sleep(time::Duration::from_secs(1));
    }
}

I get the following error:

$ cargo run
   Compiling hello_world v0.1.0 (/home/vasco/a)
error[E0615]: attempted to take value of method `show` on type `Foo`
  --> src/main.rs:17:35
   |
17 |         libc::signal(SIGUSR1, foo.show as usize);
   |                                   ^^^^ method, not a field
   |
help: use parentheses to call the method
   |
17 |         libc::signal(SIGUSR1, foo.show() as usize);
   |                                       ^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0615`.
error: could not compile `hello_world`

To learn more, run the command again with --verbose.

If I follow the advice (libc::signal(SIGUSR1, foo.show() as usize);):

$ cargo run
   Compiling hello_world v0.1.0 (/home/vasco/a)
error[E0605]: non-primitive cast: `()` as `usize`
  --> src/main.rs:17:31
   |
17 |         libc::signal(SIGUSR1, foo.show() as usize);
   |                               ^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error: aborting due to previous error

For more information about this error, try `rustc --explain E0605`.
error: could not compile `hello_world`

To learn more, run the command again with --verbose.

Using a normal function works as expected:

use libc::SIGUSR1;
use std::{thread, time};

fn show() {
    println!("Foo SIGNAL")
}

fn main() {
    unsafe {
        libc::signal(SIGUSR1, show as usize);
    }

    let delay = time::Duration::from_secs(1);
    loop{
        println!("sleeping for 1 sec");
        thread::sleep(delay);
    }
}

Any suggestions?

Thanks


Solution

  • Thanks to Ahmed Masud:

    use std::{thread, time};
    use std::sync::Arc;
    use std::sync::atomic::{AtomicBool, Ordering};
    
    fn show() {
        println!("SHOW");
    }
    
    fn main() {
    
        loop {
            // Declare bool, setting it to false
            let term = Arc::new(AtomicBool::new(false));
            // Ask signal_hook to set the term variable to true when the program receives a SIGUSR1 kill signal
            signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&term)).ok();
            show();
    
            while !term.load(Ordering::Relaxed) {
                println!("sleeping for 1 sec");
                thread::sleep(time::Duration::from_secs(1));
            }
        }
    }
    

    Seem to work as expected.

    Update 1

    To example above will repond to the signal every second, no matter when the signal was triggered, the following solves that:

    use std::io::Error;
    use std::time::{SystemTime, UNIX_EPOCH};
    use signal_hook::consts::signal::SIGUSR1;
    use signal_hook::iterator::SignalsInfo;
    use signal_hook::iterator::exfiltrator::WithOrigin;
    
    fn show() {
        let a = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis();
        println!("show: {}", a);
    }
    
    fn mainloop() {
        let handle = std::thread::spawn(|| {
            loop {
                let a = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_millis();
                println!("main: {}", a);
                std::thread::sleep(std::time::Duration::from_secs(1));
            }
        });
        handle.join().unwrap();
    }
    
    fn main() -> Result<(), Error> {
        // Subscribe to the SIGUSR1 signal
        let sigs = vec![SIGUSR1];
        let mut signals = SignalsInfo::<WithOrigin>::new(&sigs)?;
    
        // Run the application in its own thread
        mainloop();
    
        // Consume all the incoming signals (this happens in "normal" Rust thread)
        for info in &mut signals {
            match info.signal {
                SIGUSR1 => {
                    show();
                }
                _ => { // These are all the ones left
                    eprintln!("Terminating");
                    break;
                }
            }
        }
    
        Ok(())
    }
    
    $ cat Cargo.toml
    [package]
    ...
    
    [dependencies]
    signal-hook = { version = "0.3", features = ["extended-siginfo"] }