mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-25 12:00:35 +00:00
signal: make windows docs for signal module show up on unix builds (#3770)
This commit is contained in:
parent
cf02b3f32d
commit
69a6585429
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -286,6 +286,7 @@ jobs:
|
||||
- name: "doc --lib --all-features"
|
||||
run: cargo doc --lib --no-deps --all-features --document-private-items
|
||||
env:
|
||||
RUSTFLAGS: --cfg docsrs
|
||||
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
|
||||
|
||||
loom:
|
||||
|
@ -44,3 +44,8 @@ proptest = "1"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
# Issue #3770
|
||||
#
|
||||
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
|
||||
# can pick up stubbed types exported by `tokio`.
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
|
@ -36,9 +36,9 @@ cfg_signal! {
|
||||
#[cfg(unix)]
|
||||
pub use signal_unix::SignalStream;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
mod signal_windows;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
pub use signal_windows::{CtrlCStream, CtrlBreakStream};
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
//! `Signal` type for receiving notifications of signals.
|
||||
|
||||
#![cfg(unix)]
|
||||
#![cfg_attr(docsrs, doc(cfg(all(unix, feature = "signal"))))]
|
||||
|
||||
use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
|
||||
use crate::signal::RxFuture;
|
||||
|
@ -5,127 +5,22 @@
|
||||
//! `SetConsoleCtrlHandler` function which receives events of the type
|
||||
//! `CTRL_C_EVENT` and `CTRL_BREAK_EVENT`.
|
||||
|
||||
#![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,
|
||||
}
|
||||
#[cfg(not(docsrs))]
|
||||
#[path = "windows/sys.rs"]
|
||||
mod imp;
|
||||
#[cfg(not(docsrs))]
|
||||
pub(crate) use self::imp::{OsExtraData, OsStorage};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
#[cfg(docsrs)]
|
||||
#[path = "windows/stub.rs"]
|
||||
mod imp;
|
||||
|
||||
/// Creates a new stream which receives "ctrl-c" notifications sent to the
|
||||
/// process.
|
||||
@ -150,7 +45,9 @@ 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
|
||||
@ -163,7 +60,7 @@ pub fn ctrl_c() -> io::Result<CtrlC> {
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlC {
|
||||
inner: Event,
|
||||
inner: RxFuture,
|
||||
}
|
||||
|
||||
impl CtrlC {
|
||||
@ -191,7 +88,7 @@ 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
|
||||
@ -223,7 +120,7 @@ impl CtrlC {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
|
||||
self.inner.inner.poll_recv(cx)
|
||||
self.inner.poll_recv(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +134,7 @@ impl CtrlC {
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CtrlBreak {
|
||||
inner: Event,
|
||||
inner: RxFuture,
|
||||
}
|
||||
|
||||
impl CtrlBreak {
|
||||
@ -263,7 +160,7 @@ impl CtrlBreak {
|
||||
/// }
|
||||
/// ```
|
||||
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
|
||||
@ -295,7 +192,7 @@ impl CtrlBreak {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<()>> {
|
||||
self.inner.inner.poll_recv(cx)
|
||||
self.inner.poll_recv(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,56 +217,7 @@ impl CtrlBreak {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn ctrl_break() -> io::Result<CtrlBreak> {
|
||||
Event::new(CTRL_BREAK_EVENT).map(|inner| CtrlBreak { inner })
|
||||
}
|
||||
|
||||
#[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(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!(super::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(CTRL_BREAK_EVENT);
|
||||
}
|
||||
|
||||
ctrl_break.recv().await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn rt() -> Runtime {
|
||||
crate::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
Ok(CtrlBreak {
|
||||
inner: self::imp::ctrl_break()?,
|
||||
})
|
||||
}
|
||||
|
13
tokio/src/signal/windows/stub.rs
Normal file
13
tokio/src/signal/windows/stub.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! 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_c() -> io::Result<RxFuture> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
pub(super) fn ctrl_break() -> io::Result<RxFuture> {
|
||||
panic!()
|
||||
}
|
153
tokio/src/signal/windows/sys.rs
Normal file
153
tokio/src/signal/windows/sys.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::sync::Once;
|
||||
|
||||
use crate::signal::registry::{globals, EventId, EventInfo, Init, Storage};
|
||||
use crate::signal::RxFuture;
|
||||
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
|
||||
use winapi::um::consoleapi::SetConsoleCtrlHandler;
|
||||
use winapi::um::wincon::{CTRL_BREAK_EVENT, CTRL_C_EVENT};
|
||||
|
||||
pub(super) fn ctrl_c() -> io::Result<RxFuture> {
|
||||
new(CTRL_C_EVENT)
|
||||
}
|
||||
|
||||
pub(super) fn ctrl_break() -> io::Result<RxFuture> {
|
||||
new(CTRL_BREAK_EVENT)
|
||||
}
|
||||
|
||||
fn new(signum: DWORD) -> io::Result<RxFuture> {
|
||||
global_init()?;
|
||||
let rx = globals().register_listener(signum as EventId);
|
||||
Ok(RxFuture::new(rx))
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[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(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(CTRL_BREAK_EVENT);
|
||||
}
|
||||
|
||||
ctrl_break.recv().await.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn rt() -> Runtime {
|
||||
crate::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user