Pauan / rust-signals

Zero-cost functional reactive Signals for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

.signal().for_each returns type ForEach

bbros-dev opened this issue · comments

Thank you for all the effort you have put into this crate and for making it open source.

We are new to Rust, and as part of our orientation we are exploring signal handling for CLI apps. Hopefully this use case is within scope?

We're having trouble obtaining a stream or a future using the approaches suggested in the tutorial.

Specifically,

  1. ftr*2 are ForEach<...>
  2. ftr* are SignalStream<...>

We hope we haven't done something embarassing, and our exploratory code is:

// Old style tokio 0.1:
// https://riptutorial.com/rust/example/32175/tokio-example
use futures::{Future, Stream};
// For `into_future()`
use futures::stream::StreamExt;
use futures_signals::signal::SignalExt;
//use tokio_core::reactor::Core;
use tokio::signal::unix::{self as unix_signal, Signal};
//use std::thread::{self, sleep};
use std::pin::Pin;
use std::time::Duration;
// use std::sync::mpsc::{channel, Receiver};

async fn run(signals: Receiver<i32>) {
    loop {
        if let Some(signal) = signals.await {
            println!("received {} signal", signal);
        }
        tokio::time::sleep(Duration::from_millis(1)).await;
    }
}

#[tokio::main]
async fn main() {
    // Create channels for sending and receiving signals
    let (signals_tx, signals_rx) = tokio::sync::oneshot::channel();

    // Execute the program with the receiving end of the channel
    // for listening to any signals that are sent to it.
    tokio::spawn( run(signals_rx) );

    let sigint = futures_signals::signal::Mutable::new(tokio::signal::unix::SignalKind::interrupt());
    let sigterm = futures_signals::signal::Mutable::new(tokio::signal::unix::SignalKind::terminate());

    // Return a future for each signal
    // However, this returns a type ForEach<MutableSignal<SignalKind>, ..., ...>
    let ftrint = sigint.signal()
            .for_each(|s| {
                // Lets get out of here...
                println!("Trapped signal {:?} to quit. Exiting!", s);
                async { std::process::exit(1) }
            });
   let ftrterm = sigterm.signal()
            .for_each(|s| {
                // Lets get out of here...
                println!("Trapped signal {:?} to quit. Exiting!", s);
                async move { std::process::exit(1) }
            });

    // Return stream (an async iterator)
    let strmint = sigint.signal().to_stream();
    let strmterm = sigterm.signal().to_stream();

    let (sint, ftrint) = strmint.into_future().await;
    let (sterm, ftrterm) = strmterm.into_future().await;

    let ftrintbp = Box::pin(ftrint);
    let ftrtermbp = Box::pin(ftrterm);

    let vf = vec![ftrintbp, ftrtermbp];
    let signals = futures::future::select_ok(vf).await;

}
commented

We are new to Rust, and as part of our orientation we are exploring signal handling for CLI apps. Hopefully this use case is within scope?

If you're talking about UNIX signals, that is completely unrelated to this crate. You want this crate instead.

Alternatively, if you prefer using tokio, you can just use tokio + futures:

use futures::select;
use futures::stream::{Stream, StreamExt};
use tokio::signal::unix::{signal, Signal, SignalKind};
use std::task::{Poll, Context};
use std::pin::Pin;


#[derive(Debug)]
pub struct SignalStream {
    signal: Signal,
}

impl SignalStream {
    pub fn new(kind: SignalKind) -> Self {
        Self {
            signal: signal(kind).unwrap(),
        }
    }
}

impl Stream for SignalStream {
    type Item = ();

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
        self.signal.poll_recv(cx)
    }
}


#[tokio::main]
async fn main() {
    let mut interrupts = SignalStream::new(SignalKind::interrupt()).fuse();
    let mut terminates = SignalStream::new(SignalKind::terminate()).fuse();

    loop {
        select! {
            _ = interrupts.next() => {
                println!("INTERRUPT");
            },
            _ = terminates.next() => {
                println!("TERMINATE");
            },
            complete => break,
        }
    }
}

I'm surprised that tokio doesn't provide a SignalStream type already, but it's easy to define (as shown above).

Thanks for the helpful pointers. We're trying different crates for signal handling and obviously have mistaken the scope and role of this crate. Apologies for the noise, and thanks again for the code sample - we'll study that too.