From 49129434198a96444bc0e9582a14062d3a46e93a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Fri, 27 Nov 2020 19:53:17 +0000 Subject: signal: expose CtrlC stream on windows (#3186) * Make tokio::signal::windows::ctrl_c() public. * Stop referring to private tokio::signal::windows::Event in module documentation. Closes #3178 --- tokio/src/signal/windows.rs | 127 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 11 deletions(-) diff --git a/tokio/src/signal/windows.rs b/tokio/src/signal/windows.rs index 1e783622..7f2e4862 100644 --- a/tokio/src/signal/windows.rs +++ b/tokio/src/signal/windows.rs @@ -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)] @@ -79,10 +79,6 @@ pub(crate) struct Event { rx: Receiver<()>, } -pub(crate) fn ctrl_c() -> io::Result { - Event::new(CTRL_C_EVENT) -} - impl Event { fn new(signum: DWORD) -> io::Result { global_init()?; @@ -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> { +/// // 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 { + 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> { + /// // 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 { + /// println!("polling MyFuture"); + /// self.ctrl_c.poll_recv(cx) + /// } + /// } + /// ``` + pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll> { + 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> { + self.poll_recv(cx) + } + } +} + /// Represents a stream which receives "ctrl-break" notifications sent to the process /// via `SetConsoleCtrlHandler`. /// @@ -163,7 +269,7 @@ 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"); @@ -171,8 +277,7 @@ impl CtrlBreak { /// } /// ``` 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 @@ -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"); -- cgit v1.2.3