Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signal: update documentation with caveats #1854

Merged
merged 4 commits into from Nov 28, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions tokio/src/signal/ctrl_c.rs
Expand Up @@ -15,6 +15,24 @@ use std::io;
/// future will complete on the first received `ctrl-c` **after** the initial
/// call to either `Future::poll` or `.await`.
///
/// # Caveats
///
/// On Unix platforms, the first time that a `Signal` instance is registered for a
/// particular signal kind, an OS signal-handler is installed which replaces the
/// default platform behavior when that signal is received, **for the duration of
/// the entire process**.
///
/// For example, Unix systems will terminate a process by default when it
/// receives a signal generated by "CTRL+C" on the terminal. But, when a
/// `ctrl_c` stream is created to listen for this signal, the time it arrives,
/// it will be translated to a stream event, and the process will continue to
/// execute. **Even if this `Signal` instance is dropped, subsequent SIGINT
/// deliveries will end up captured by Tokio, and the default platform behavior
/// will NOT be reset**.
///
/// Thus, applications should take care to ensure the expected signal behavior
/// occurs as expected after listening for specific signals.
///
/// # Examples
///
/// ```rust,no_run
Expand Down
9 changes: 2 additions & 7 deletions tokio/src/signal/mod.rs
@@ -1,16 +1,12 @@
//! Asynchronous signal handling for Tokio
//!
//! The primary type exported from this crate, `unix::Signal`, allows
//! listening for arbitrary signals on Unix platforms, receiving them
//! in an asynchronous fashion.
//!
//! Note that signal handling is in general a very tricky topic and should be
//! used with great care. This crate attempts to implement 'best practice' for
//! signal handling, but it should be evaluated for your own applications' needs
//! to see if it's suitable.
//!
//! The are some fundamental limitations of this crate documented on the
//! `Signal` structure as well.
//! The are some fundamental limitations of this crate documented on the OS
//! specific structures, as well.
//!
//! # Examples
//!
Expand All @@ -31,7 +27,6 @@
//!
//! ```rust,no_run
//! # #[cfg(unix)] {
//!
//! use tokio::signal::unix::{signal, SignalKind};
//!
//! #[tokio::main]
Expand Down
107 changes: 92 additions & 15 deletions tokio/src/signal/unix.rs
Expand Up @@ -309,12 +309,7 @@ impl Driver {
}
}

/// An implementation of `Stream` for receiving a particular type of signal.
///
/// This structure implements the `Stream` trait and represents notifications
/// of the current process receiving a particular signal. The signal being
/// listened for is passed to `Signal::new`, and the same signal number is then
/// yielded as each element for the stream.
/// A stream of events for receiving a particular type of OS signal.
///
/// 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
Expand All @@ -336,13 +331,46 @@ impl Driver {
/// improvements are possible in this crate, it's recommended to not plan on
/// having millions of signal channels open.
///
/// * Currently the "driver task" to process incoming signals never exits. This
/// driver task runs in the background of the event loop provided, and
/// in general you shouldn't need to worry about it.
///
/// If you've got any questions about this feel free to open an issue on the
/// repo, though, as I'd love to chat about this! In other words, I'd love to
/// alleviate some of these limitations if possible!
/// repo! New approaches to alleviate some of these limitations are always
/// appreciated!
///
/// # Caveats
///
/// The first time that a `Signal` instance is registered for a particular
/// signal kind, an OS signal-handler is installed which replaces the default
/// platform behavior when that signal is received, **for the duration of the
/// entire process**.
///
/// For example, Unix systems will terminate a process by default when it
/// receives SIGINT. But, when a `Signal` instance is created to listen for
/// this signal, the next SIGINT that arrives will be translated to a stream
/// event, and the process will continue to execute. **Even if this `Signal`
/// instance is dropped, subsequent SIGINT deliveries will end up captured by
/// Tokio, and the default platform behavior will NOT be reset**.
///
/// Thus, applications should take care to ensure the expected signal behavior
/// occurs as expected after listening for specific signals.
///
/// # Examples
///
/// Wait for SIGHUP
///
/// ```rust,no_run
/// use tokio::signal::unix::{signal, SignalKind};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of hangup signals.
/// let mut stream = signal(SignalKind::hangup())?;
///
/// // Print whenever a HUP signal is received
/// loop {
/// stream.recv().await;
/// println!("got signal HUP");
/// }
/// }
/// ```
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct Signal {
Expand All @@ -351,7 +379,7 @@ pub struct Signal {
}

/// Creates a new stream which will receive notifications when the current
/// process receives the signal `signal`.
/// process receives the specified signal `kind`.
///
/// This function will create a new stream which binds to the default reactor.
/// The `Signal` stream is an infinite stream which will receive
Expand Down Expand Up @@ -391,13 +419,62 @@ pub fn signal(kind: SignalKind) -> io::Result<Signal> {
}

impl Signal {
#[doc(hidden)] // TODO: Dox
/// Receive the next signal notification event.
///
/// `None` is returned if no more events can be received by this stream.
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// Wait for SIGHUP
///
/// ```rust,no_run
/// use tokio::signal::unix::{signal, SignalKind};
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of hangup signals.
/// let mut stream = signal(SignalKind::hangup())?;
///
/// // Print whenever a HUP signal is received
/// loop {
/// stream.recv().await;
/// println!("got signal HUP");
/// }
/// }
/// ```
pub async fn recv(&mut self) -> Option<()> {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_recv(cx)).await
}

#[doc(hidden)] // TODO: document
/// Poll to receive the next signal notification event, outside of an
/// `async` context.
///
/// `None` is returned if no more events can be received by this stream.
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// Polling from a manually implemented future
///
/// ```rust,no_run
/// use std::pin::Pin;
/// use std::future::Future;
/// use std::task::{Context, Poll};
/// use tokio::signal::unix::Signal;
///
/// struct MyFuture {
/// signal: Signal,
/// }
///
/// impl Future for MyFuture {
/// type Output = Option<()>;
///
/// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
/// println!("polling MyFuture");
/// self.signal.poll_recv(cx)
/// }
/// }
/// ```
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
let _ = self.driver.poll(cx);
self.rx.poll_recv(cx)
Expand Down
87 changes: 78 additions & 9 deletions tokio/src/signal/windows.rs
Expand Up @@ -73,7 +73,6 @@ impl Init for OsExtraData {
/// processed quickly enough. This means that if two notifications are
/// received back-to-back, then the stream may only receive one item about the
/// two notifications.
// FIXME: refactor and combine with unix::Signal
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub(crate) struct Event {
Expand Down Expand Up @@ -139,7 +138,7 @@ unsafe extern "system" fn handler(ty: DWORD) -> BOOL {
/// Represents a stream which receives "ctrl-break" notifications sent to the process
/// via `SetConsoleCtrlHandler`.
///
/// A notification to this process notifies *all* streams listening to
/// A notification to this process notifies *all* streams listening for
/// this event. Moreover, the notifications **are coalesced** if they aren't processed
/// quickly enough. This means that if two notifications are received back-to-back,
/// then the stream may only receive one item about the two notifications.
Expand All @@ -150,25 +149,95 @@ pub struct CtrlBreak {
}

impl CtrlBreak {
#[doc(hidden)] // TODO: document
/// Receive the next signal notification event.
///
/// `None` is returned if no more events can be received by this stream.
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal::windows::ctrl_break;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of CTRL-BREAK events.
/// let mut stream = ctrl_break()?;
///
/// // Print whenever a CTRL-BREAK event is received
/// loop {
/// stream.recv().await;
/// println!("got signal CTRL-BREAK");
/// }
/// }
/// ```
pub async fn recv(&mut self) -> Option<()> {
use crate::future::poll_fn;
poll_fn(|cx| self.poll_recv(cx)).await
}

/// Poll to receive the next signal notification event, outside of an
/// `async` context.
///
/// `None` is returned if no more events can be received by this stream.
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Examples
///
/// Polling from a manually implemented future
///
/// ```rust,no_run
/// use std::pin::Pin;
/// use std::future::Future;
/// use std::task::{Context, Poll};
/// use tokio::signal::windows::CtrlBreak;
///
/// struct MyFuture {
/// ctrlBreak: CtrlBreak,
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
/// }
///
/// impl Future for MyFuture {
/// type Output = Option<()>;
///
/// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
/// println!("polling MyFuture");
/// self.ctrlBreak.poll_recv(cx)
ipetkov marked this conversation as resolved.
Show resolved Hide resolved
/// }
/// }
/// ```
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.inner.rx.poll_recv(cx)
}
}

#[cfg(feature = "stream")]
impl futures_core::Stream for CtrlBreak {
type Item = ();
cfg_stream! {
impl futures_core::Stream for CtrlBreak {
type Item = ();

fn poll_next(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.poll_recv(cx)
fn poll_next(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.poll_recv(cx)
}
}
}

/// Creates a new stream which receives "ctrl-break" notifications sent to the
/// process.
///
/// This function binds to the default reactor.
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal::windows::ctrl_break;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of CTRL-BREAK events.
/// let mut stream = ctrl_break()?;
///
/// // Print whenever a CTRL-BREAK event is received
/// loop {
/// stream.recv().await;
/// println!("got signal CTRL-BREAK");
/// }
/// }
/// ```
pub fn ctrl_break() -> io::Result<CtrlBreak> {
Event::new(CTRL_BREAK_EVENT).map(|inner| CtrlBreak { inner })
}
Expand Down