Repository: tokio-rs/tokio
File: tokio/src/signal/unix.rs (Rust)

Code before:
    // Check that we have a signal driver running
    handle.check_inner()?;

    let globals = globals();
    let siginfo = match globals.storage().get(signal as EventId) {
        Some(slot) => slot,
        None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
    };

    let mut error = None;
    siginfo
        .init
        .get_or_init(|| {
            let result =
                unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) };
            if let Err(e) = result {
                error = Some(e);
                Err(())
            } else {
                Ok(())
            }
        })
        .map_err(|_| {
            error.unwrap_or_else(|| {
                Error::new(ErrorKind::Other, "registering signal handler failed")
            })
        })
}

/// An listener for receiving a particular type of OS signal.
///
/// The listener can be turned into a `Stream` using [`SignalStream`].
///
/// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html
///
/// In general signal handling on Unix is a pretty tricky topic, and this
/// structure is no exception! There are some important limitations to keep in
/// mind when using `Signal` streams:
///
/// * Signals handling in Unix already necessitates coalescing signals
///   together sometimes. This `Signal` stream is also no exception here in
///   that it will also coalesce signals. That is, even if the signal handler
///   for this process runs multiple times, the `Signal` stream may only return
///   one signal notification. Specifically, before `poll` is called, all
///   signal notifications are coalesced into one item returned from `poll`.
///   Once `poll` has been called, however, a further signal is guaranteed to
///   be yielded as an item.
///
///   Put another way, any element pulled off the returned listener corresponds to
///   *at least one* signal, but possibly more.
///

Review comment (bug):
If we're going to make the effort to remember that this has failed in the past, we might as well return the same error that was raised (i.e. we should be capturing the last error in the OnceLock) instead of "minting" our own `Other` error here

Code after:
    // Check that we have a signal driver running
    handle.check_inner()?;

    let globals = globals();
    let siginfo = match globals.storage().get(signal as EventId) {
        Some(slot) => slot,
        None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
    };

    siginfo
        .init
        .get_or_init(|| {
            unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) }
                .map(|_| ())
                .map_err(|e| e.raw_os_error())
        })
        .map_err(|e| {
            e.map_or_else(
                || Error::new(ErrorKind::Other, "registering signal handler failed"),
                Error::from_raw_os_error,
            )
        })
}

/// An listener for receiving a particular type of OS signal.
///
/// The listener can be turned into a `Stream` using [`SignalStream`].
///
/// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html
///
/// In general signal handling on Unix is a pretty tricky topic, and this
/// structure is no exception! There are some important limitations to keep in
/// mind when using `Signal` streams:
///
/// * Signals handling in Unix already necessitates coalescing signals
///   together sometimes. This `Signal` stream is also no exception here in
///   that it will also coalesce signals. That is, even if the signal handler
///   for this process runs multiple times, the `Signal` stream may only return
///   one signal notification. Specifically, before `poll` is called, all
///   signal notifications are coalesced into one item returned from `poll`.
///   Once `poll` has been called, however, a further signal is guaranteed to
///   be yielded as an item.
///
///   Put another way, any element pulled off the returned listener corresponds to
///   *at least one* signal, but possibly more.
///
/// * Signal handling in general is relatively inefficient. Although some
///   improvements are possible in this crate, it's recommended to not plan on
///   having millions of signal channels open.
///
/// If you've got any questions about this feel free to open an issue on the