diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:31 +0000 |
commit | dc0db358abe19481e475e10c32149b53370f1a1c (patch) | |
tree | ab8ce99c4b255ce46f99ef402c27916055b899ee /vendor/tokio/src/signal | |
parent | Releasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/tokio/src/signal')
-rw-r--r-- | vendor/tokio/src/signal/ctrl_c.rs | 9 | ||||
-rw-r--r-- | vendor/tokio/src/signal/mod.rs | 4 | ||||
-rw-r--r-- | vendor/tokio/src/signal/registry.rs | 43 | ||||
-rw-r--r-- | vendor/tokio/src/signal/reusable_box.rs | 12 | ||||
-rw-r--r-- | vendor/tokio/src/signal/unix.rs | 116 | ||||
-rw-r--r-- | vendor/tokio/src/signal/unix/driver.rs | 207 | ||||
-rw-r--r-- | vendor/tokio/src/signal/windows.rs | 525 | ||||
-rw-r--r-- | vendor/tokio/src/signal/windows/stub.rs | 25 | ||||
-rw-r--r-- | vendor/tokio/src/signal/windows/sys.rs | 229 |
9 files changed, 719 insertions, 451 deletions
diff --git a/vendor/tokio/src/signal/ctrl_c.rs b/vendor/tokio/src/signal/ctrl_c.rs index 1eeeb85aa..b26ab7ead 100644 --- a/vendor/tokio/src/signal/ctrl_c.rs +++ b/vendor/tokio/src/signal/ctrl_c.rs @@ -47,6 +47,15 @@ use std::io; /// println!("received ctrl-c event"); /// } /// ``` +/// +/// Listen in the background: +/// +/// ```rust,no_run +/// tokio::spawn(async move { +/// tokio::signal::ctrl_c().await.unwrap(); +/// // Your handler here +/// }); +/// ``` pub async fn ctrl_c() -> io::Result<()> { os_impl::ctrl_c()?.recv().await; Ok(()) diff --git a/vendor/tokio/src/signal/mod.rs b/vendor/tokio/src/signal/mod.rs index fe572f041..3aacc60ef 100644 --- a/vendor/tokio/src/signal/mod.rs +++ b/vendor/tokio/src/signal/mod.rs @@ -1,4 +1,4 @@ -//! Asynchronous signal handling for Tokio +//! Asynchronous signal handling for Tokio. //! //! 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 @@ -48,7 +48,7 @@ use std::task::{Context, Poll}; mod ctrl_c; pub use ctrl_c::ctrl_c; -mod registry; +pub(crate) mod registry; mod os { #[cfg(unix)] diff --git a/vendor/tokio/src/signal/registry.rs b/vendor/tokio/src/signal/registry.rs index 8b89108a6..48e98c832 100644 --- a/vendor/tokio/src/signal/registry.rs +++ b/vendor/tokio/src/signal/registry.rs @@ -1,12 +1,10 @@ #![allow(clippy::unit_arg)] use crate::signal::os::{OsExtraData, OsStorage}; - use crate::sync::watch; +use crate::util::once_cell::OnceCell; -use once_cell::sync::Lazy; use std::ops; -use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; pub(crate) type EventId = usize; @@ -152,19 +150,25 @@ impl Globals { } } -pub(crate) fn globals() -> Pin<&'static Globals> +fn globals_init() -> Globals where OsExtraData: 'static + Send + Sync + Init, OsStorage: 'static + Send + Sync + Init, { - static GLOBALS: Lazy<Pin<Box<Globals>>> = Lazy::new(|| { - Box::pin(Globals { - extra: OsExtraData::init(), - registry: Registry::new(OsStorage::init()), - }) - }); - - GLOBALS.as_ref() + Globals { + extra: OsExtraData::init(), + registry: Registry::new(OsStorage::init()), + } +} + +pub(crate) fn globals() -> &'static Globals +where + OsExtraData: 'static + Send + Sync + Init, + OsStorage: 'static + Send + Sync + Init, +{ + static GLOBALS: OnceCell<Globals> = OnceCell::new(); + + GLOBALS.get(globals_init) } #[cfg(all(test, not(loom)))] @@ -202,7 +206,12 @@ mod tests { registry.broadcast(); // Yield so the previous broadcast can get received - crate::time::sleep(std::time::Duration::from_millis(10)).await; + // + // This yields many times since the block_on task is only polled every 61 + // ticks. + for _ in 0..100 { + crate::task::yield_now().await; + } // Send subsequent signal registry.record_event(0); @@ -232,7 +241,7 @@ mod tests { #[test] fn record_invalid_event_does_nothing() { let registry = Registry::new(vec![EventInfo::default()]); - registry.record_event(42); + registry.record_event(1302); } #[test] @@ -240,17 +249,17 @@ mod tests { let registry = Registry::new(vec![EventInfo::default(), EventInfo::default()]); registry.record_event(0); - assert_eq!(false, registry.broadcast()); + assert!(!registry.broadcast()); let first = registry.register_listener(0); let second = registry.register_listener(1); registry.record_event(0); - assert_eq!(true, registry.broadcast()); + assert!(registry.broadcast()); drop(first); registry.record_event(0); - assert_eq!(false, registry.broadcast()); + assert!(!registry.broadcast()); drop(second); } diff --git a/vendor/tokio/src/signal/reusable_box.rs b/vendor/tokio/src/signal/reusable_box.rs index 426ecb06f..796fa210b 100644 --- a/vendor/tokio/src/signal/reusable_box.rs +++ b/vendor/tokio/src/signal/reusable_box.rs @@ -30,7 +30,7 @@ impl<T> ReusableBoxFuture<T> { Self { boxed } } - /// Replace the future currently stored in this box. + /// Replaces the future currently stored in this box. /// /// This reallocates if and only if the layout of the provided future is /// different from the layout of the currently stored future. @@ -43,7 +43,7 @@ impl<T> ReusableBoxFuture<T> { } } - /// Replace the future currently stored in this box. + /// Replaces the future currently stored in this box. /// /// This function never reallocates, but returns an error if the provided /// future has a different size or alignment from the currently stored @@ -70,7 +70,7 @@ impl<T> ReusableBoxFuture<T> { } } - /// Set the current future. + /// Sets the current future. /// /// # Safety /// @@ -103,14 +103,14 @@ impl<T> ReusableBoxFuture<T> { } } - /// Get a pinned reference to the underlying future. + /// Gets a pinned reference to the underlying future. pub(crate) fn get_pin(&mut self) -> Pin<&mut (dyn Future<Output = T> + Send)> { // SAFETY: The user of this box cannot move the box, and we do not move it // either. unsafe { Pin::new_unchecked(self.boxed.as_mut()) } } - /// Poll the future stored inside this box. + /// Polls the future stored inside this box. pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> { self.get_pin().poll(cx) } @@ -119,7 +119,7 @@ impl<T> ReusableBoxFuture<T> { impl<T> Future for ReusableBoxFuture<T> { type Output = T; - /// Poll the future stored inside this box. + /// Polls the future stored inside this box. fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { Pin::into_inner(self).get_pin().poll(cx) } diff --git a/vendor/tokio/src/signal/unix.rs b/vendor/tokio/src/signal/unix.rs index f96b2f4c2..ae5c13085 100644 --- a/vendor/tokio/src/signal/unix.rs +++ b/vendor/tokio/src/signal/unix.rs @@ -4,30 +4,33 @@ //! `Signal` type for receiving notifications of signals. #![cfg(unix)] +#![cfg_attr(docsrs, doc(cfg(all(unix, feature = "signal"))))] +use crate::runtime::scheduler; +use crate::runtime::signal::Handle; use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage}; use crate::signal::RxFuture; use crate::sync::watch; use mio::net::UnixStream; use std::io::{self, Error, ErrorKind, Write}; -use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; use std::task::{Context, Poll}; -pub(crate) mod driver; -use self::driver::Handle; - pub(crate) type OsStorage = Vec<SignalInfo>; -// Number of different unix signals -// (FreeBSD has 33) -const SIGNUM: usize = 33; - impl Init for OsStorage { fn init() -> Self { - (0..SIGNUM).map(|_| SignalInfo::default()).collect() + // There are reliable signals ranging from 1 to 33 available on every Unix platform. + #[cfg(not(target_os = "linux"))] + let possible = 0..=33; + + // On Linux, there are additional real-time signals available. + #[cfg(target_os = "linux")] + let possible = 0..=libc::SIGRTMAX(); + + possible.map(|_| SignalInfo::default()).collect() } } @@ -47,7 +50,7 @@ impl Storage for OsStorage { #[derive(Debug)] pub(crate) struct OsExtraData { sender: UnixStream, - receiver: UnixStream, + pub(crate) receiver: UnixStream, } impl Init for OsExtraData { @@ -59,7 +62,7 @@ impl Init for OsExtraData { } /// Represents the specific kind of signal to listen for. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct SignalKind(libc::c_int); impl SignalKind { @@ -79,15 +82,26 @@ impl SignalKind { // unlikely to change to other types, but technically libc can change this // in the future minor version. // See https://github.com/tokio-rs/tokio/issues/3767 for more. - pub fn from_raw(signum: std::os::raw::c_int) -> Self { + pub const fn from_raw(signum: std::os::raw::c_int) -> Self { Self(signum as libc::c_int) } + /// Get the signal's numeric value. + /// + /// ```rust + /// # use tokio::signal::unix::SignalKind; + /// let kind = SignalKind::interrupt(); + /// assert_eq!(kind.as_raw_value(), libc::SIGINT); + /// ``` + pub const fn as_raw_value(&self) -> std::os::raw::c_int { + self.0 + } + /// Represents the SIGALRM signal. /// /// On Unix systems this signal is sent when a real-time timer has expired. /// By default, the process is terminated by this signal. - pub fn alarm() -> Self { + pub const fn alarm() -> Self { Self(libc::SIGALRM) } @@ -95,7 +109,7 @@ impl SignalKind { /// /// On Unix systems this signal is sent when the status of a child process /// has changed. By default, this signal is ignored. - pub fn child() -> Self { + pub const fn child() -> Self { Self(libc::SIGCHLD) } @@ -103,7 +117,7 @@ impl SignalKind { /// /// On Unix systems this signal is sent when the terminal is disconnected. /// By default, the process is terminated by this signal. - pub fn hangup() -> Self { + pub const fn hangup() -> Self { Self(libc::SIGHUP) } @@ -118,7 +132,7 @@ impl SignalKind { target_os = "netbsd", target_os = "openbsd" ))] - pub fn info() -> Self { + pub const fn info() -> Self { Self(libc::SIGINFO) } @@ -126,7 +140,7 @@ impl SignalKind { /// /// On Unix systems this signal is sent to interrupt a program. /// By default, the process is terminated by this signal. - pub fn interrupt() -> Self { + pub const fn interrupt() -> Self { Self(libc::SIGINT) } @@ -134,7 +148,7 @@ impl SignalKind { /// /// On Unix systems this signal is sent when I/O operations are possible /// on some file descriptor. By default, this signal is ignored. - pub fn io() -> Self { + pub const fn io() -> Self { Self(libc::SIGIO) } @@ -143,7 +157,7 @@ impl SignalKind { /// On Unix systems this signal is sent when the process attempts to write /// to a pipe which has no reader. By default, the process is terminated by /// this signal. - pub fn pipe() -> Self { + pub const fn pipe() -> Self { Self(libc::SIGPIPE) } @@ -152,7 +166,7 @@ impl SignalKind { /// On Unix systems this signal is sent to issue a shutdown of the /// process, after which the OS will dump the process core. /// By default, the process is terminated by this signal. - pub fn quit() -> Self { + pub const fn quit() -> Self { Self(libc::SIGQUIT) } @@ -160,7 +174,7 @@ impl SignalKind { /// /// On Unix systems this signal is sent to issue a shutdown of the /// process. By default, the process is terminated by this signal. - pub fn terminate() -> Self { + pub const fn terminate() -> Self { Self(libc::SIGTERM) } @@ -168,7 +182,7 @@ impl SignalKind { /// /// On Unix systems this is a user defined signal. /// By default, the process is terminated by this signal. - pub fn user_defined1() -> Self { + pub const fn user_defined1() -> Self { Self(libc::SIGUSR1) } @@ -176,7 +190,7 @@ impl SignalKind { /// /// On Unix systems this is a user defined signal. /// By default, the process is terminated by this signal. - pub fn user_defined2() -> Self { + pub const fn user_defined2() -> Self { Self(libc::SIGUSR2) } @@ -184,11 +198,23 @@ impl SignalKind { /// /// On Unix systems this signal is sent when the terminal window is resized. /// By default, this signal is ignored. - pub fn window_change() -> Self { + pub const fn window_change() -> Self { Self(libc::SIGWINCH) } } +impl From<std::os::raw::c_int> for SignalKind { + fn from(signum: std::os::raw::c_int) -> Self { + Self::from_raw(signum as libc::c_int) + } +} + +impl From<SignalKind> for std::os::raw::c_int { + fn from(kind: SignalKind) -> Self { + kind.as_raw_value() + } +} + pub(crate) struct SignalInfo { event_info: EventInfo, init: Once, @@ -213,7 +239,7 @@ impl Default for SignalInfo { /// 2. Wake up the driver by writing a byte to a pipe /// /// Those two operations should both be async-signal safe. -fn action(globals: Pin<&'static Globals>, signal: libc::c_int) { +fn action(globals: &'static Globals, signal: libc::c_int) { globals.record_event(signal as EventId); // Send a wakeup, ignore any errors (anything reasonably possible is @@ -266,7 +292,11 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { } } -/// A stream of events for receiving a particular type of OS signal. +/// 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 @@ -281,7 +311,7 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { /// 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 stream corresponds to +/// 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 @@ -319,11 +349,11 @@ fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> { /// #[tokio::main] /// async fn main() -> Result<(), Box<dyn std::error::Error>> { /// // An infinite stream of hangup signals. -/// let mut stream = signal(SignalKind::hangup())?; +/// let mut sig = signal(SignalKind::hangup())?; /// /// // Print whenever a HUP signal is received /// loop { -/// stream.recv().await; +/// sig.recv().await; /// println!("got signal HUP"); /// } /// } @@ -334,7 +364,7 @@ pub struct Signal { inner: RxFuture, } -/// Creates a new stream which will receive notifications when the current +/// Creates a new listener which will receive notifications when the current /// process receives the specified signal `kind`. /// /// This function will create a new stream which binds to the default reactor. @@ -356,8 +386,15 @@ pub struct Signal { /// * If the previous initialization of this specific signal failed. /// * If the signal is one of /// [`signal_hook::FORBIDDEN`](fn@signal_hook_registry::register#panics) +/// +/// # Panics +/// +/// This function panics if there is no current reactor set, or if the `rt` +/// feature flag is not enabled. +#[track_caller] pub fn signal(kind: SignalKind) -> io::Result<Signal> { - let rx = signal_with_handle(kind, &Handle::current())?; + let handle = scheduler::Handle::current(); + let rx = signal_with_handle(kind, handle.driver().signal())?; Ok(Signal { inner: RxFuture::new(rx), @@ -379,6 +416,12 @@ impl Signal { /// /// `None` is returned if no more events can be received by this stream. /// + /// # Cancel safety + /// + /// This method is cancel safe. If you use it as the event in a + /// [`tokio::select!`](crate::select) statement and some other branch + /// completes first, then it is guaranteed that no signal is lost. + /// /// # Examples /// /// Wait for SIGHUP @@ -473,4 +516,15 @@ mod tests { ) .unwrap_err(); } + + #[test] + fn from_c_int() { + assert_eq!(SignalKind::from(2), SignalKind::interrupt()); + } + + #[test] + fn into_c_int() { + let value: std::os::raw::c_int = SignalKind::interrupt().into(); + assert_eq!(value, libc::SIGINT as _); + } } diff --git a/vendor/tokio/src/signal/unix/driver.rs b/vendor/tokio/src/signal/unix/driver.rs deleted file mode 100644 index 5fe7c354c..000000000 --- a/vendor/tokio/src/signal/unix/driver.rs +++ /dev/null @@ -1,207 +0,0 @@ -#![cfg_attr(not(feature = "rt"), allow(dead_code))] - -//! Signal driver - -use crate::io::driver::{Driver as IoDriver, Interest}; -use crate::io::PollEvented; -use crate::park::Park; -use crate::signal::registry::globals; - -use mio::net::UnixStream; -use std::io::{self, Read}; -use std::ptr; -use std::sync::{Arc, Weak}; -use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; -use std::time::Duration; - -/// Responsible for registering wakeups when an OS signal is received, and -/// subsequently dispatching notifications to any signal listeners as appropriate. -/// -/// Note: this driver relies on having an enabled IO driver in order to listen to -/// pipe write wakeups. -#[derive(Debug)] -pub(crate) struct Driver { - /// Thread parker. The `Driver` park implementation delegates to this. - park: IoDriver, - - /// A pipe for receiving wake events from the signal handler - receiver: PollEvented<UnixStream>, - - /// Shared state - inner: Arc<Inner>, -} - -#[derive(Clone, Debug, Default)] -pub(crate) struct Handle { - inner: Weak<Inner>, -} - -#[derive(Debug)] -pub(super) struct Inner(()); - -// ===== impl Driver ===== - -impl Driver { - /// Creates a new signal `Driver` instance that delegates wakeups to `park`. - pub(crate) fn new(park: IoDriver) -> io::Result<Self> { - use std::mem::ManuallyDrop; - use std::os::unix::io::{AsRawFd, FromRawFd}; - - // NB: We give each driver a "fresh" receiver file descriptor to avoid - // the issues described in alexcrichton/tokio-process#42. - // - // In the past we would reuse the actual receiver file descriptor and - // swallow any errors around double registration of the same descriptor. - // I'm not sure if the second (failed) registration simply doesn't end - // up receiving wake up notifications, or there could be some race - // condition when consuming readiness events, but having distinct - // descriptors for distinct PollEvented instances appears to mitigate - // this. - // - // Unfortunately we cannot just use a single global PollEvented instance - // either, since we can't compare Handles or assume they will always - // point to the exact same reactor. - // - // Mio 0.7 removed `try_clone()` as an API due to unexpected behavior - // with registering dups with the same reactor. In this case, duping is - // safe as each dup is registered with separate reactors **and** we - // only expect at least one dup to receive the notification. - - // Manually drop as we don't actually own this instance of UnixStream. - let receiver_fd = globals().receiver.as_raw_fd(); - - // safety: there is nothing unsafe about this, but the `from_raw_fd` fn is marked as unsafe. - let original = - ManuallyDrop::new(unsafe { std::os::unix::net::UnixStream::from_raw_fd(receiver_fd) }); - let receiver = UnixStream::from_std(original.try_clone()?); - let receiver = PollEvented::new_with_interest_and_handle( - receiver, - Interest::READABLE | Interest::WRITABLE, - park.handle(), - )?; - - Ok(Self { - park, - receiver, - inner: Arc::new(Inner(())), - }) - } - - /// Returns a handle to this event loop which can be sent across threads - /// and can be used as a proxy to the event loop itself. - pub(crate) fn handle(&self) -> Handle { - Handle { - inner: Arc::downgrade(&self.inner), - } - } - - fn process(&self) { - // Check if the pipe is ready to read and therefore has "woken" us up - // - // To do so, we will `poll_read_ready` with a noop waker, since we don't - // need to actually be notified when read ready... - let waker = unsafe { Waker::from_raw(RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE)) }; - let mut cx = Context::from_waker(&waker); - - let ev = match self.receiver.registration().poll_read_ready(&mut cx) { - Poll::Ready(Ok(ev)) => ev, - Poll::Ready(Err(e)) => panic!("reactor gone: {}", e), - Poll::Pending => return, // No wake has arrived, bail - }; - - // Drain the pipe completely so we can receive a new readiness event - // if another signal has come in. - let mut buf = [0; 128]; - loop { - match (&*self.receiver).read(&mut buf) { - Ok(0) => panic!("EOF on self-pipe"), - Ok(_) => continue, // Keep reading - Err(e) if e.kind() == io::ErrorKind::WouldBlock => break, - Err(e) => panic!("Bad read on self-pipe: {}", e), - } - } - - self.receiver.registration().clear_readiness(ev); - - // Broadcast any signals which were received - globals().broadcast(); - } -} - -const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); - -unsafe fn noop_clone(_data: *const ()) -> RawWaker { - RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE) -} - -unsafe fn noop(_data: *const ()) {} - -// ===== impl Park for Driver ===== - -impl Park for Driver { - type Unpark = <IoDriver as Park>::Unpark; - type Error = io::Error; - - fn unpark(&self) -> Self::Unpark { - self.park.unpark() - } - - fn park(&mut self) -> Result<(), Self::Error> { - self.park.park()?; - self.process(); - Ok(()) - } - - fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> { - self.park.park_timeout(duration)?; - self.process(); - Ok(()) - } - - fn shutdown(&mut self) { - self.park.shutdown() - } -} - -// ===== impl Handle ===== - -impl Handle { - pub(super) fn check_inner(&self) -> io::Result<()> { - if self.inner.strong_count() > 0 { - Ok(()) - } else { - Err(io::Error::new(io::ErrorKind::Other, "signal driver gone")) - } - } -} - -cfg_rt! { - impl Handle { - /// Returns a handle to the current driver - /// - /// # Panics - /// - /// This function panics if there is no current signal driver set. - pub(super) fn current() -> Self { - crate::runtime::context::signal_handle().expect( - "there is no signal driver running, must be called from the context of Tokio runtime", - ) - } - } -} - -cfg_not_rt! { - impl Handle { - /// Returns a handle to the current driver - /// - /// # Panics - /// - /// This function panics if there is no current signal driver set. - pub(super) fn current() -> Self { - panic!( - "there is no signal driver running, must be called from the context of Tokio runtime or with\ - `rt` enabled.", - ) - } - } -} diff --git a/vendor/tokio/src/signal/windows.rs b/vendor/tokio/src/signal/windows.rs index c231d6268..2f70f98b1 100644 --- a/vendor/tokio/src/signal/windows.rs +++ b/vendor/tokio/src/signal/windows.rs @@ -1,133 +1,28 @@ //! Windows-specific types for signal handling. //! -//! 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`. +//! This module is only defined on Windows and allows receiving "ctrl-c", +//! "ctrl-break", "ctrl-logoff", "ctrl-shutdown", and "ctrl-close" +//! notifications. These events are listened for via the `SetConsoleCtrlHandler` +//! function which receives the corresponding windows_sys event type. -#![cfg(windows)] +#![cfg(any(windows, docsrs))] +#![cfg_attr(docsrs, doc(cfg(all(windows, feature = "signal"))))] -use crate::signal::registry::{globals, EventId, EventInfo, Init, Storage}; use crate::signal::RxFuture; - -use std::convert::TryFrom; use std::io; -use std::sync::Once; use std::task::{Context, Poll}; -use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE}; -use winapi::um::consoleapi::SetConsoleCtrlHandler; -use winapi::um::wincon::{CTRL_BREAK_EVENT, CTRL_C_EVENT}; - -#[derive(Debug)] -pub(crate) struct OsStorage { - ctrl_c: EventInfo, - ctrl_break: EventInfo, -} -impl Init for OsStorage { - fn init() -> Self { - Self { - ctrl_c: EventInfo::default(), - ctrl_break: EventInfo::default(), - } - } -} - -impl Storage for OsStorage { - fn event_info(&self, id: EventId) -> Option<&EventInfo> { - match DWORD::try_from(id) { - Ok(CTRL_C_EVENT) => Some(&self.ctrl_c), - Ok(CTRL_BREAK_EVENT) => Some(&self.ctrl_break), - _ => None, - } - } +#[cfg(not(docsrs))] +#[path = "windows/sys.rs"] +mod imp; +#[cfg(not(docsrs))] +pub(crate) use self::imp::{OsExtraData, OsStorage}; - fn for_each<'a, F>(&'a self, mut f: F) - where - F: FnMut(&'a EventInfo), - { - f(&self.ctrl_c); - f(&self.ctrl_break); - } -} - -#[derive(Debug)] -pub(crate) struct OsExtraData {} - -impl Init for OsExtraData { - fn init() -> Self { - Self {} - } -} +#[cfg(docsrs)] +#[path = "windows/stub.rs"] +mod imp; -/// Stream of events discovered via `SetConsoleCtrlHandler`. -/// -/// This structure can be used to listen for events of the type `CTRL_C_EVENT` -/// and `CTRL_BREAK_EVENT`. The `Stream` trait is implemented for this struct -/// and will resolve for each notification received by the process. Note that -/// there are few limitations with this as well: -/// -/// * A notification to this process notifies *all* `Event` streams for that -/// event type. -/// * Notifications to an `Event` stream **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(crate) struct Event { - inner: RxFuture, -} - -impl Event { - fn new(signum: DWORD) -> io::Result<Self> { - global_init()?; - - let rx = globals().register_listener(signum as EventId); - - Ok(Self { - inner: RxFuture::new(rx), - }) - } -} - -fn global_init() -> io::Result<()> { - static INIT: Once = Once::new(); - - let mut init = None; - - INIT.call_once(|| unsafe { - let rc = SetConsoleCtrlHandler(Some(handler), TRUE); - let ret = if rc == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(()) - }; - - init = Some(ret); - }); - - init.unwrap_or_else(|| Ok(())) -} - -unsafe extern "system" fn handler(ty: DWORD) -> BOOL { - let globals = globals(); - globals.record_event(ty as EventId); - - // According to https://docs.microsoft.com/en-us/windows/console/handlerroutine - // the handler routine is always invoked in a new thread, thus we don't - // have the same restrictions as in Unix signal handlers, meaning we can - // go ahead and perform the broadcast here. - if globals.broadcast() { - TRUE - } else { - // No one is listening for this notification any more - // let the OS fire the next (possibly the default) handler. - FALSE - } -} - -/// Creates a new stream which receives "ctrl-c" notifications sent to the +/// Creates a new listener which receives "ctrl-c" notifications sent to the /// process. /// /// # Examples @@ -137,12 +32,12 @@ unsafe extern "system" fn handler(ty: DWORD) -> BOOL { /// /// #[tokio::main] /// async fn main() -> Result<(), Box<dyn std::error::Error>> { -/// // An infinite stream of CTRL-C events. -/// let mut stream = ctrl_c()?; +/// // A listener of CTRL-C events. +/// let mut signal = ctrl_c()?; /// /// // Print whenever a CTRL-C event is received. /// for countdown in (0..3).rev() { -/// stream.recv().await; +/// signal.recv().await; /// println!("got CTRL-C. {} more to exit", countdown); /// } /// @@ -150,26 +45,32 @@ unsafe extern "system" fn handler(ty: DWORD) -> BOOL { /// } /// ``` pub fn ctrl_c() -> io::Result<CtrlC> { - Event::new(CTRL_C_EVENT).map(|inner| CtrlC { inner }) + Ok(CtrlC { + inner: self::imp::ctrl_c()?, + }) } -/// Represents a stream which receives "ctrl-c" notifications sent to the process +/// Represents a listener which receives "ctrl-c" notifications sent to the process /// via `SetConsoleCtrlHandler`. /// -/// A notification to this process notifies *all* streams listening for +/// This event can be turned into a `Stream` using [`CtrlCStream`]. +/// +/// [`CtrlCStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.CtrlCStream.html +/// +/// A notification to this process notifies *all* receivers 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"] +/// then the listener may only receive one item about the two notifications. +#[must_use = "listeners do nothing unless polled"] #[derive(Debug)] pub struct CtrlC { - inner: Event, + inner: RxFuture, } impl CtrlC { /// Receives the next signal notification event. /// - /// `None` is returned if no more events can be received by this stream. + /// `None` is returned if no more events can be received by the listener. /// /// # Examples /// @@ -178,12 +79,11 @@ impl CtrlC { /// /// #[tokio::main] /// async fn main() -> Result<(), Box<dyn std::error::Error>> { - /// // An infinite stream of CTRL-C events. - /// let mut stream = ctrl_c()?; + /// let mut signal = ctrl_c()?; /// /// // Print whenever a CTRL-C event is received. /// for countdown in (0..3).rev() { - /// stream.recv().await; + /// signal.recv().await; /// println!("got CTRL-C. {} more to exit", countdown); /// } /// @@ -191,13 +91,13 @@ impl CtrlC { /// } /// ``` pub async fn recv(&mut self) -> Option<()> { - self.inner.inner.recv().await + 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. + /// `None` is returned if no more events can be received. /// /// # Examples /// @@ -223,27 +123,31 @@ impl CtrlC { /// } /// ``` pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { - self.inner.inner.poll_recv(cx) + self.inner.poll_recv(cx) } } -/// Represents a stream which receives "ctrl-break" notifications sent to the process +/// Represents a listener which receives "ctrl-break" notifications sent to the process /// via `SetConsoleCtrlHandler`. /// -/// A notification to this process notifies *all* streams listening for +/// This listener can be turned into a `Stream` using [`CtrlBreakStream`]. +/// +/// [`CtrlBreakStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.CtrlBreakStream.html +/// +/// A notification to this process notifies *all* receivers 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"] +/// then the listener may only receive one item about the two notifications. +#[must_use = "listeners do nothing unless polled"] #[derive(Debug)] pub struct CtrlBreak { - inner: Event, + inner: RxFuture, } impl CtrlBreak { /// Receives the next signal notification event. /// - /// `None` is returned if no more events can be received by this stream. + /// `None` is returned if no more events can be received by this listener. /// /// # Examples /// @@ -252,24 +156,24 @@ impl CtrlBreak { /// /// #[tokio::main] /// async fn main() -> Result<(), Box<dyn std::error::Error>> { - /// // An infinite stream of CTRL-BREAK events. - /// let mut stream = ctrl_break()?; + /// // A listener of CTRL-BREAK events. + /// let mut signal = ctrl_break()?; /// /// // Print whenever a CTRL-BREAK event is received. /// loop { - /// stream.recv().await; + /// signal.recv().await; /// println!("got signal CTRL-BREAK"); /// } /// } /// ``` pub async fn recv(&mut self) -> Option<()> { - self.inner.inner.recv().await + 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. + /// `None` is returned if no more events can be received by this listener. /// /// # Examples /// @@ -295,11 +199,11 @@ impl CtrlBreak { /// } /// ``` pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { - self.inner.inner.poll_recv(cx) + self.inner.poll_recv(cx) } } -/// Creates a new stream which receives "ctrl-break" notifications sent to the +/// Creates a new listener which receives "ctrl-break" notifications sent to the /// process. /// /// # Examples @@ -309,67 +213,312 @@ impl CtrlBreak { /// /// #[tokio::main] /// async fn main() -> Result<(), Box<dyn std::error::Error>> { -/// // An infinite stream of CTRL-BREAK events. -/// let mut stream = ctrl_break()?; +/// // A listener of CTRL-BREAK events. +/// let mut signal = ctrl_break()?; /// /// // Print whenever a CTRL-BREAK event is received. /// loop { -/// stream.recv().await; +/// signal.recv().await; /// println!("got signal CTRL-BREAK"); /// } /// } /// ``` pub fn ctrl_break() -> io::Result<CtrlBreak> { - Event::new(CTRL_BREAK_EVENT).map(|inner| CtrlBreak { inner }) + Ok(CtrlBreak { + inner: self::imp::ctrl_break()?, + }) } -#[cfg(all(test, not(loom)))] -mod tests { - use super::*; - use crate::runtime::Runtime; +/// Creates a new listener which receives "ctrl-close" notifications sent to the +/// process. +/// +/// # Examples +/// +/// ```rust,no_run +/// use tokio::signal::windows::ctrl_close; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box<dyn std::error::Error>> { +/// // A listener of CTRL-CLOSE events. +/// let mut signal = ctrl_close()?; +/// +/// // Print whenever a CTRL-CLOSE event is received. +/// for countdown in (0..3).rev() { +/// signal.recv().await; +/// println!("got CTRL-CLOSE. {} more to exit", countdown); +/// } +/// +/// Ok(()) +/// } +/// ``` +pub fn ctrl_close() -> io::Result<CtrlClose> { + Ok(CtrlClose { + inner: self::imp::ctrl_close()?, + }) +} - use tokio_test::{assert_ok, assert_pending, assert_ready_ok, task}; +/// Represents a listener which receives "ctrl-close" notitifications sent to the process +/// via 'SetConsoleCtrlHandler'. +/// +/// A notification to this process notifies *all* listeners 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 listener may only receive one item about the two notifications. +#[must_use = "listeners do nothing unless polled"] +#[derive(Debug)] +pub struct CtrlClose { + inner: RxFuture, +} - #[test] - fn ctrl_c() { - let rt = rt(); - let _enter = rt.enter(); +impl CtrlClose { + /// Receives the next signal notification event. + /// + /// `None` is returned if no more events can be received by this listener. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tokio::signal::windows::ctrl_close; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box<dyn std::error::Error>> { + /// // A listener of CTRL-CLOSE events. + /// let mut signal = ctrl_close()?; + /// + /// // Print whenever a CTRL-CLOSE event is received. + /// signal.recv().await; + /// println!("got CTRL-CLOSE. Cleaning up before exiting"); + /// + /// Ok(()) + /// } + /// ``` + pub async fn recv(&mut self) -> Option<()> { + self.inner.recv().await + } - let mut ctrl_c = task::spawn(crate::signal::ctrl_c()); + /// 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 listener. + /// + /// # 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::CtrlClose; + /// + /// struct MyFuture { + /// ctrl_close: CtrlClose, + /// } + /// + /// 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_close.poll_recv(cx) + /// } + /// } + /// ``` + pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { + self.inner.poll_recv(cx) + } +} - assert_pending!(ctrl_c.poll()); +/// Creates a new listener which receives "ctrl-shutdown" notifications sent to the +/// process. +/// +/// # Examples +/// +/// ```rust,no_run +/// use tokio::signal::windows::ctrl_shutdown; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box<dyn std::error::Error>> { +/// // A listener of CTRL-SHUTDOWN events. +/// let mut signal = ctrl_shutdown()?; +/// +/// signal.recv().await; +/// println!("got CTRL-SHUTDOWN. Cleaning up before exiting"); +/// +/// Ok(()) +/// } +/// ``` +pub fn ctrl_shutdown() -> io::Result<CtrlShutdown> { + Ok(CtrlShutdown { + inner: self::imp::ctrl_shutdown()?, + }) +} - // Windows doesn't have a good programmatic way of sending events - // like sending signals on Unix, so we'll stub out the actual OS - // integration and test that our handling works. - unsafe { - super::handler(CTRL_C_EVENT); - } +/// Represents a listener which receives "ctrl-shutdown" notitifications sent to the process +/// via 'SetConsoleCtrlHandler'. +/// +/// A notification to this process notifies *all* listeners 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 listener may only receive one item about the two notifications. +#[must_use = "listeners do nothing unless polled"] +#[derive(Debug)] +pub struct CtrlShutdown { + inner: RxFuture, +} - assert_ready_ok!(ctrl_c.poll()); +impl CtrlShutdown { + /// Receives the next signal notification event. + /// + /// `None` is returned if no more events can be received by this listener. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tokio::signal::windows::ctrl_shutdown; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box<dyn std::error::Error>> { + /// // A listener of CTRL-SHUTDOWN events. + /// let mut signal = ctrl_shutdown()?; + /// + /// // Print whenever a CTRL-SHUTDOWN event is received. + /// signal.recv().await; + /// println!("got CTRL-SHUTDOWN. Cleaning up before exiting"); + /// + /// Ok(()) + /// } + /// ``` + pub async fn recv(&mut self) -> Option<()> { + self.inner.recv().await } - #[test] - fn ctrl_break() { - let rt = rt(); + /// 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 listener. + /// + /// # 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::CtrlShutdown; + /// + /// struct MyFuture { + /// ctrl_shutdown: CtrlShutdown, + /// } + /// + /// 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_shutdown.poll_recv(cx) + /// } + /// } + /// ``` + pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { + self.inner.poll_recv(cx) + } +} - rt.block_on(async { - let mut ctrl_break = assert_ok!(super::ctrl_break()); +/// Creates a new listener which receives "ctrl-logoff" notifications sent to the +/// process. +/// +/// # Examples +/// +/// ```rust,no_run +/// use tokio::signal::windows::ctrl_logoff; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box<dyn std::error::Error>> { +/// // A listener of CTRL-LOGOFF events. +/// let mut signal = ctrl_logoff()?; +/// +/// signal.recv().await; +/// println!("got CTRL-LOGOFF. Cleaning up before exiting"); +/// +/// Ok(()) +/// } +/// ``` +pub fn ctrl_logoff() -> io::Result<CtrlLogoff> { + Ok(CtrlLogoff { + inner: self::imp::ctrl_logoff()?, + }) +} - // Windows doesn't have a good programmatic way of sending events - // like sending signals on Unix, so we'll stub out the actual OS - // integration and test that our handling works. - unsafe { - super::handler(CTRL_BREAK_EVENT); - } +/// Represents a listener which receives "ctrl-logoff" notitifications sent to the process +/// via 'SetConsoleCtrlHandler'. +/// +/// A notification to this process notifies *all* listeners 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 listener may only receive one item about the two notifications. +#[must_use = "listeners do nothing unless polled"] +#[derive(Debug)] +pub struct CtrlLogoff { + inner: RxFuture, +} - ctrl_break.recv().await.unwrap(); - }); +impl CtrlLogoff { + /// Receives the next signal notification event. + /// + /// `None` is returned if no more events can be received by this listener. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tokio::signal::windows::ctrl_logoff; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box<dyn std::error::Error>> { + /// // An listener of CTRL-LOGOFF events. + /// let mut signal = ctrl_logoff()?; + /// + /// // Print whenever a CTRL-LOGOFF event is received. + /// signal.recv().await; + /// println!("got CTRL-LOGOFF. Cleaning up before exiting"); + /// + /// Ok(()) + /// } + /// ``` + pub async fn recv(&mut self) -> Option<()> { + self.inner.recv().await } - fn rt() -> Runtime { - crate::runtime::Builder::new_current_thread() - .build() - .unwrap() + /// 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 listener. + /// + /// # 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::CtrlLogoff; + /// + /// struct MyFuture { + /// ctrl_logoff: CtrlLogoff, + /// } + /// + /// 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_logoff.poll_recv(cx) + /// } + /// } + /// ``` + pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> { + self.inner.poll_recv(cx) } } diff --git a/vendor/tokio/src/signal/windows/stub.rs b/vendor/tokio/src/signal/windows/stub.rs new file mode 100644 index 000000000..61df30979 --- /dev/null +++ b/vendor/tokio/src/signal/windows/stub.rs @@ -0,0 +1,25 @@ +//! Stub implementations for the platform API so that rustdoc can build linkable +//! documentation on non-windows platforms. + +use crate::signal::RxFuture; +use std::io; + +pub(super) fn ctrl_break() -> io::Result<RxFuture> { + panic!() +} + +pub(super) fn ctrl_close() -> io::Result<RxFuture> { + panic!() +} + +pub(super) fn ctrl_c() -> io::Result<RxFuture> { + panic!() +} + +pub(super) fn ctrl_logoff() -> io::Result<RxFuture> { + panic!() +} + +pub(super) fn ctrl_shutdown() -> io::Result<RxFuture> { + panic!() +} diff --git a/vendor/tokio/src/signal/windows/sys.rs b/vendor/tokio/src/signal/windows/sys.rs new file mode 100644 index 000000000..26e6bdf81 --- /dev/null +++ b/vendor/tokio/src/signal/windows/sys.rs @@ -0,0 +1,229 @@ +use std::io; +use std::sync::Once; + +use crate::signal::registry::{globals, EventId, EventInfo, Init, Storage}; +use crate::signal::RxFuture; + +use windows_sys::Win32::Foundation::BOOL; +use windows_sys::Win32::System::Console as console; + +pub(super) fn ctrl_break() -> io::Result<RxFuture> { + new(console::CTRL_BREAK_EVENT) +} + +pub(super) fn ctrl_close() -> io::Result<RxFuture> { + new(console::CTRL_CLOSE_EVENT) +} + +pub(super) fn ctrl_c() -> io::Result<RxFuture> { + new(console::CTRL_C_EVENT) +} + +pub(super) fn ctrl_logoff() -> io::Result<RxFuture> { + new(console::CTRL_LOGOFF_EVENT) +} + +pub(super) fn ctrl_shutdown() -> io::Result<RxFuture> { + new(console::CTRL_SHUTDOWN_EVENT) +} + +fn new(signum: u32) -> io::Result<RxFuture> { + global_init()?; + let rx = globals().register_listener(signum as EventId); + Ok(RxFuture::new(rx)) +} + +#[derive(Debug)] +pub(crate) struct OsStorage { + ctrl_break: EventInfo, + ctrl_close: EventInfo, + ctrl_c: EventInfo, + ctrl_logoff: EventInfo, + ctrl_shutdown: EventInfo, +} + +impl Init for OsStorage { + fn init() -> Self { + Self { + ctrl_break: Default::default(), + ctrl_close: Default::default(), + ctrl_c: Default::default(), + ctrl_logoff: Default::default(), + ctrl_shutdown: Default::default(), + } + } +} + +impl Storage for OsStorage { + fn event_info(&self, id: EventId) -> Option<&EventInfo> { + match u32::try_from(id) { + Ok(console::CTRL_BREAK_EVENT) => Some(&self.ctrl_break), + Ok(console::CTRL_CLOSE_EVENT) => Some(&self.ctrl_close), + Ok(console::CTRL_C_EVENT) => Some(&self.ctrl_c), + Ok(console::CTRL_LOGOFF_EVENT) => Some(&self.ctrl_logoff), + Ok(console::CTRL_SHUTDOWN_EVENT) => Some(&self.ctrl_shutdown), + _ => None, + } + } + + fn for_each<'a, F>(&'a self, mut f: F) + where + F: FnMut(&'a EventInfo), + { + f(&self.ctrl_break); + f(&self.ctrl_close); + f(&self.ctrl_c); + f(&self.ctrl_logoff); + f(&self.ctrl_shutdown); + } +} + +#[derive(Debug)] +pub(crate) struct OsExtraData {} + +impl Init for OsExtraData { + fn init() -> Self { + Self {} + } +} + +fn global_init() -> io::Result<()> { + static INIT: Once = Once::new(); + + let mut init = None; + + INIT.call_once(|| unsafe { + let rc = console::SetConsoleCtrlHandler(Some(handler), 1); + let ret = if rc == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + }; + + init = Some(ret); + }); + + init.unwrap_or_else(|| Ok(())) +} + +unsafe extern "system" fn handler(ty: u32) -> BOOL { + let globals = globals(); + globals.record_event(ty as EventId); + + // According to https://docs.microsoft.com/en-us/windows/console/handlerroutine + // the handler routine is always invoked in a new thread, thus we don't + // have the same restrictions as in Unix signal handlers, meaning we can + // go ahead and perform the broadcast here. + if globals.broadcast() { + 1 + } else { + // No one is listening for this notification any more + // let the OS fire the next (possibly the default) handler. + 0 + } +} + +#[cfg(all(test, not(loom)))] +mod tests { + use super::*; + use crate::runtime::Runtime; + + use tokio_test::{assert_ok, assert_pending, assert_ready_ok, task}; + + #[test] + fn ctrl_c() { + let rt = rt(); + let _enter = rt.enter(); + + let mut ctrl_c = task::spawn(crate::signal::ctrl_c()); + + assert_pending!(ctrl_c.poll()); + + // Windows doesn't have a good programmatic way of sending events + // like sending signals on Unix, so we'll stub out the actual OS + // integration and test that our handling works. + unsafe { + super::handler(console::CTRL_C_EVENT); + } + + assert_ready_ok!(ctrl_c.poll()); + } + + #[test] + fn ctrl_break() { + let rt = rt(); + + rt.block_on(async { + let mut ctrl_break = assert_ok!(crate::signal::windows::ctrl_break()); + + // Windows doesn't have a good programmatic way of sending events + // like sending signals on Unix, so we'll stub out the actual OS + // integration and test that our handling works. + unsafe { + super::handler(console::CTRL_BREAK_EVENT); + } + + ctrl_break.recv().await.unwrap(); + }); + } + + #[test] + fn ctrl_close() { + let rt = rt(); + + rt.block_on(async { + let mut ctrl_close = assert_ok!(crate::signal::windows::ctrl_close()); + + // Windows doesn't have a good programmatic way of sending events + // like sending signals on Unix, so we'll stub out the actual OS + // integration and test that our handling works. + unsafe { + super::handler(console::CTRL_CLOSE_EVENT); + } + + ctrl_close.recv().await.unwrap(); + }); + } + + #[test] + fn ctrl_shutdown() { + let rt = rt(); + + rt.block_on(async { + let mut ctrl_shutdown = assert_ok!(crate::signal::windows::ctrl_shutdown()); + + // Windows doesn't have a good programmatic way of sending events + // like sending signals on Unix, so we'll stub out the actual OS + // integration and test that our handling works. + unsafe { + super::handler(console::CTRL_SHUTDOWN_EVENT); + } + + ctrl_shutdown.recv().await.unwrap(); + }); + } + + #[test] + fn ctrl_logoff() { + let rt = rt(); + + rt.block_on(async { + let mut ctrl_logoff = assert_ok!(crate::signal::windows::ctrl_logoff()); + + // Windows doesn't have a good programmatic way of sending events + // like sending signals on Unix, so we'll stub out the actual OS + // integration and test that our handling works. + unsafe { + super::handler(console::CTRL_LOGOFF_EVENT); + } + + ctrl_logoff.recv().await.unwrap(); + }); + } + + fn rt() -> Runtime { + crate::runtime::Builder::new_current_thread() + .build() + .unwrap() + } +} |