Skip to content

Commit

Permalink
signal: expose CtrlC stream on windows (#3186)
Browse files Browse the repository at this point in the history
* Make tokio::signal::windows::ctrl_c() public.
* Stop referring to private tokio::signal::windows::Event in module
  documentation.

Closes #3178
  • Loading branch information
niklasf committed Nov 27, 2020
1 parent 5e406a7 commit 4912943
Showing 1 changed file with 116 additions and 11 deletions.
127 changes: 116 additions & 11 deletions tokio/src/signal/windows.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Windows-specific types for signal handling.
//!
//! This module is only defined on Windows and contains the primary `Event` type
//! for receiving notifications of events. These events are listened for via the
//! This module is only defined on Windows and allows receiving "ctrl-c"
//! and "ctrl-break" notifications. These events are listened for via the
//! `SetConsoleCtrlHandler` function which receives events of the type
//! `CTRL_C_EVENT` and `CTRL_BREAK_EVENT`
//! `CTRL_C_EVENT` and `CTRL_BREAK_EVENT`.

#![cfg(windows)]

Expand Down Expand Up @@ -79,10 +79,6 @@ pub(crate) struct Event {
rx: Receiver<()>,
}

pub(crate) fn ctrl_c() -> io::Result<Event> {
Event::new(CTRL_C_EVENT)
}

impl Event {
fn new(signum: DWORD) -> io::Result<Self> {
global_init()?;
Expand Down Expand Up @@ -135,6 +131,116 @@ unsafe extern "system" fn handler(ty: DWORD) -> BOOL {
}
}

/// Creates a new stream which receives "ctrl-c" notifications sent to the
/// process.
///
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal::windows::ctrl_c;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of CTRL-C events.
/// let mut stream = ctrl_c()?;
///
/// // Print whenever a CTRL-C event is received.
/// for countdown in (0..3).rev() {
/// stream.recv().await;
/// println!("got CTRL-C. {} more to exit", countdown);
/// }
///
/// Ok(())
/// }
/// ```
pub fn ctrl_c() -> io::Result<CtrlC> {
Event::new(CTRL_C_EVENT).map(|inner| CtrlC { inner })
}

/// Represents a stream which receives "ctrl-c" notifications sent to the process
/// via `SetConsoleCtrlHandler`.
///
/// 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.
#[must_use = "streams do nothing unless polled"]
#[derive(Debug)]
pub struct CtrlC {
inner: Event,
}

impl CtrlC {
/// Receives the next signal notification event.
///
/// `None` is returned if no more events can be received by this stream.
///
/// # Examples
///
/// ```rust,no_run
/// use tokio::signal::windows::ctrl_c;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // An infinite stream of CTRL-C events.
/// let mut stream = ctrl_c()?;
///
/// // Print whenever a CTRL-C event is received.
/// for countdown in (0..3).rev() {
/// stream.recv().await;
/// println!("got CTRL-C. {} more to exit", countdown);
/// }
///
/// Ok(())
/// }
/// ```
pub async fn recv(&mut self) -> Option<()> {
self.inner.recv().await
}

/// Polls 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.
///
/// # 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::CtrlC;
///
/// struct MyFuture {
/// ctrl_c: CtrlC,
/// }
///
/// impl Future for MyFuture {
/// type Output = Option<()>;
///
/// fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
/// println!("polling MyFuture");
/// self.ctrl_c.poll_recv(cx)
/// }
/// }
/// ```
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
self.inner.rx.poll_recv(cx)
}
}

cfg_stream! {
impl crate::stream::Stream for CtrlC {
type Item = ();

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

/// Represents a stream which receives "ctrl-break" notifications sent to the process
/// via `SetConsoleCtrlHandler`.
///
Expand Down Expand Up @@ -163,16 +269,15 @@ impl CtrlBreak {
/// // An infinite stream of CTRL-BREAK events.
/// let mut stream = ctrl_break()?;
///
/// // Print whenever a CTRL-BREAK event is received
/// // 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
self.inner.recv().await
}

/// Polls to receive the next signal notification event, outside of an
Expand Down Expand Up @@ -231,7 +336,7 @@ cfg_stream! {
/// // An infinite stream of CTRL-BREAK events.
/// let mut stream = ctrl_break()?;
///
/// // Print whenever a CTRL-BREAK event is received
/// // Print whenever a CTRL-BREAK event is received.
/// loop {
/// stream.recv().await;
/// println!("got signal CTRL-BREAK");
Expand Down

0 comments on commit 4912943

Please sign in to comment.