mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-07 00:00:09 +02:00
signal: migrate to std::futures (#1218)
Migrate to std::futures and the futures 0.3 preview and use async/await where possible **Breaking change:** the IoFuture and IoStream definitions used to refer to Box<dyn Future> and Box<dyn Stream>, but now they are defined as Pin<...> versions which are technically breaking. No other breaking or functional changes have been made
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ members = [
|
|||||||
"tokio-io",
|
"tokio-io",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"tokio-reactor",
|
"tokio-reactor",
|
||||||
# "tokio-signal",
|
"tokio-signal",
|
||||||
"tokio-sync",
|
"tokio-sync",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
"tokio-threadpool",
|
"tokio-threadpool",
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ jobs:
|
|||||||
crates:
|
crates:
|
||||||
# - tokio-fs
|
# - tokio-fs
|
||||||
tokio-reactor: []
|
tokio-reactor: []
|
||||||
# - tokio-signal
|
tokio-signal: []
|
||||||
tokio-tcp:
|
tokio-tcp:
|
||||||
- incoming
|
- incoming
|
||||||
# - tokio-tls
|
# - tokio-tls
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ travis-ci = { repository = "tokio-rs/tokio" }
|
|||||||
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.1.11"
|
futures-core-preview = "0.3.0-alpha.16"
|
||||||
|
futures-util-preview = "0.3.0-alpha.16"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
tokio-reactor = { version = "0.2.0", path = "../tokio-reactor" }
|
tokio-reactor = { version = "0.2.0", path = "../tokio-reactor" }
|
||||||
tokio-executor = { version = "0.2.0", path = "../tokio-executor" }
|
tokio-executor = { version = "0.2.0", path = "../tokio-executor" }
|
||||||
tokio-io = { version = "0.2.0", path = "../tokio-io" }
|
tokio-io = { version = "0.2.0", path = "../tokio-io" }
|
||||||
|
tokio-sync = { version = "0.2.0", path = "../tokio-sync" }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
@@ -41,6 +43,7 @@ signal-hook-registry = "~1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "0.2.0", path = "../tokio" }
|
tokio = { version = "0.2.0", path = "../tokio" }
|
||||||
tokio-timer = { version = "0.3.0", path = "../tokio-timer" }
|
tokio-timer = { version = "0.3.0", path = "../tokio-timer" }
|
||||||
|
tokio-sync = { version = "0.2.0", path = "../tokio-sync", features = ["async-traits"]}
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.winapi]
|
[target.'cfg(windows)'.dependencies.winapi]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
use tokio;
|
use futures_util::future;
|
||||||
use tokio_signal;
|
use futures_util::stream::StreamExt;
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
|
||||||
|
|
||||||
/// how many signals to handle before exiting
|
/// how many signals to handle before exiting
|
||||||
const STOP_AFTER: u64 = 10;
|
const STOP_AFTER: u64 = 10;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// tokio_signal provides a convenience builder for Ctrl+C
|
// tokio_signal provides a convenience builder for Ctrl+C
|
||||||
// this even works cross-platform: linux and windows!
|
// this even works cross-platform: linux and windows!
|
||||||
//
|
//
|
||||||
// `fn ctrl_c()` produces a `Future` of the actual stream-initialisation
|
// `fn ctrl_c()` produces a `Future` of the actual stream-initialisation
|
||||||
// the `flatten_stream()` convenience method lazily defers that
|
// so first we await until the signal is ready.
|
||||||
// initialisation, allowing us to use it 'as if' it is already the
|
let endless_stream = tokio_signal::ctrl_c().await?;
|
||||||
// stream we want, reducing boilerplate Future-handling.
|
|
||||||
let endless_stream = tokio_signal::ctrl_c().flatten_stream();
|
|
||||||
// don't keep going forever: convert the endless stream to a bounded one.
|
// don't keep going forever: convert the endless stream to a bounded one.
|
||||||
let limited_stream = endless_stream.take(STOP_AFTER);
|
let limited_stream = endless_stream.take(STOP_AFTER);
|
||||||
|
|
||||||
@@ -36,30 +34,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// Stream::for_each is a powerful primitive provided by the Futures crate.
|
// Stream::for_each is a powerful primitive provided by the Futures crate.
|
||||||
// It turns a Stream into a Future that completes after all stream-items
|
// It turns a Stream into a Future that completes after all stream-items
|
||||||
// have been completed, or the first time the closure returns an error
|
// have been completed, or the first time the closure returns an error
|
||||||
let future = limited_stream.for_each(|()| {
|
let future = limited_stream
|
||||||
// Note how we manipulate the counter without any fancy synchronisation.
|
.map(|result| result.expect("failed to get event"))
|
||||||
// The borrowchecker realises there can't be any conflicts, so the closure
|
.for_each(|()| {
|
||||||
// can just capture it.
|
// Note how we manipulate the counter without any fancy synchronisation.
|
||||||
counter += 1;
|
// The borrowchecker realises there can't be any conflicts, so the closure
|
||||||
println!(
|
// can just capture it.
|
||||||
"Ctrl+C received {} times! {} more before exit",
|
counter += 1;
|
||||||
counter,
|
println!(
|
||||||
STOP_AFTER - counter
|
"Ctrl+C received {} times! {} more before exit",
|
||||||
);
|
counter,
|
||||||
|
STOP_AFTER - counter
|
||||||
|
);
|
||||||
|
|
||||||
// return Ok-result to continue handling the stream
|
// return a result to continue handling the stream
|
||||||
Ok(())
|
future::ready(())
|
||||||
});
|
});
|
||||||
|
|
||||||
// Up until now, we haven't really DONE anything, just prepared
|
// Up until now, we haven't really DONE anything, just prepared
|
||||||
// now it's time to actually schedule, and thus execute, the stream
|
// now it's time to actually the results!
|
||||||
// on our event loop
|
future.await;
|
||||||
// FIXME(1000): windows uses a global driver task which doesn't terminate
|
|
||||||
// on its own, so if we use block_on_all our application will never exit
|
|
||||||
//tokio::runtime::current_thread::block_on_all(future)?;
|
|
||||||
tokio::runtime::current_thread::Runtime::new()
|
|
||||||
.expect("failed to start runtime on current thread")
|
|
||||||
.block_on(future)?;
|
|
||||||
|
|
||||||
println!("Stream ended, quiting the program.");
|
println!("Stream ended, quiting the program.");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
//! A small example of how to listen for two signals at the same time
|
//! A small example of how to listen for two signals at the same time
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
// A trick to not fail build on non-unix platforms when using unix-specific features.
|
// A trick to not fail build on non-unix platforms when using unix-specific features.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod platform {
|
mod platform {
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures_util::stream::{self, StreamExt};
|
||||||
|
use std::error::Error;
|
||||||
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};
|
use tokio_signal::unix::{Signal, SIGINT, SIGTERM};
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn (::std::error::Error)>> {
|
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// Create a stream for each of the signals we'd like to handle.
|
// Create a stream for each of the signals we'd like to handle.
|
||||||
let sigint = Signal::new(SIGINT).flatten_stream();
|
let sigint = Signal::new(SIGINT).await?;
|
||||||
let sigterm = Signal::new(SIGTERM).flatten_stream();
|
let sigterm = Signal::new(SIGTERM).await?;
|
||||||
|
|
||||||
// Use the `select` combinator to merge these two streams into one
|
// Use the `select` combinator to merge these two streams into one
|
||||||
let stream = sigint.select(sigterm);
|
let stream = stream::select(sigint, sigterm);
|
||||||
|
|
||||||
// Wait for a signal to arrive
|
// Wait for a signal to arrive
|
||||||
println!("Waiting for SIGINT or SIGTERM");
|
println!("Waiting for SIGINT or SIGTERM");
|
||||||
@@ -24,8 +28,7 @@ mod platform {
|
|||||||
to send a SIGINT to all processes named 'multiple' \
|
to send a SIGINT to all processes named 'multiple' \
|
||||||
(i.e. this binary)"
|
(i.e. this binary)"
|
||||||
);
|
);
|
||||||
let (item, _rest) = ::tokio::runtime::current_thread::block_on_all(stream.into_future())
|
let (item, _rest) = stream.into_future().await;
|
||||||
.map_err(|_| "failed to wait for signals")?;
|
|
||||||
|
|
||||||
// Figure out which signal we received
|
// Figure out which signal we received
|
||||||
let item = item.ok_or("received no signal")?;
|
let item = item.ok_or("received no signal")?;
|
||||||
@@ -35,6 +38,7 @@ mod platform {
|
|||||||
assert_eq!(item, SIGTERM);
|
assert_eq!(item, SIGTERM);
|
||||||
println!("received SIGTERM");
|
println!("received SIGTERM");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,11 +46,13 @@ mod platform {
|
|||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
mod platform {
|
mod platform {
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
use std::error::Error;
|
||||||
|
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
#[tokio::main]
|
||||||
platform::main()
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
platform::main().await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
// A trick to not fail build on non-unix platforms when using unix-specific features.
|
// A trick to not fail build on non-unix platforms when using unix-specific features.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod platform {
|
mod platform {
|
||||||
|
|
||||||
use futures::{Future, Stream};
|
use futures_util::future;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use std::error::Error;
|
||||||
use tokio_signal::unix::{Signal, SIGHUP};
|
use tokio_signal::unix::{Signal, SIGHUP};
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn (::std::error::Error)>> {
|
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// on Unix, we can listen to whatever signal we want, in this case: SIGHUP
|
// on Unix, we can listen to whatever signal we want, in this case: SIGHUP
|
||||||
let stream = Signal::new(SIGHUP).flatten_stream();
|
let stream = Signal::new(SIGHUP).await?;
|
||||||
|
|
||||||
println!("Waiting for SIGHUPS (Ctrl+C to quit)");
|
println!("Waiting for SIGHUPS (Ctrl+C to quit)");
|
||||||
println!(
|
println!(
|
||||||
@@ -27,25 +32,27 @@ mod platform {
|
|||||||
or something",
|
or something",
|
||||||
the_signal
|
the_signal
|
||||||
);
|
);
|
||||||
Ok(())
|
|
||||||
|
future::ready(())
|
||||||
});
|
});
|
||||||
|
|
||||||
// Up until now, we haven't really DONE anything, just prepared
|
// Up until now, we haven't really DONE anything, just prepared
|
||||||
// now it's time to actually schedule, and thus execute, the stream
|
// now it's time to actually the results!
|
||||||
// on our event loop, and loop forever
|
future.await;
|
||||||
::tokio::runtime::current_thread::block_on_all(future)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
mod platform {
|
mod platform {
|
||||||
pub fn main() -> Result<(), Box<dyn ::std::error::Error>> {
|
use std::error::Error;
|
||||||
|
pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
#[tokio::main]
|
||||||
platform::main()
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
platform::main().await
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-35
@@ -1,6 +1,7 @@
|
|||||||
#![doc(html_root_url = "https://docs.rs/tokio-signal/0.2.8")]
|
#![doc(html_root_url = "https://docs.rs/tokio-signal/0.2.8")]
|
||||||
#![deny(missing_docs, rust_2018_idioms)]
|
#![deny(missing_docs, rust_2018_idioms)]
|
||||||
#![cfg_attr(test, deny(warnings))]
|
#![cfg_attr(test, deny(warnings))]
|
||||||
|
#![cfg_attr(test, feature(async_await))]
|
||||||
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
#![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))]
|
||||||
|
|
||||||
//! Asynchronous signal handling for Tokio
|
//! Asynchronous signal handling for Tokio
|
||||||
@@ -23,43 +24,77 @@
|
|||||||
//! Print out all ctrl-C notifications received
|
//! Print out all ctrl-C notifications received
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use futures::{Future, Stream};
|
//! #![feature(async_await)]
|
||||||
//!
|
//!
|
||||||
//! // Create an infinite stream of "Ctrl+C" notifications. Each item received
|
//! use futures_util::future;
|
||||||
//! // on this stream may represent multiple ctrl-c signals.
|
//! use futures_util::stream::StreamExt;
|
||||||
//! let ctrl_c = tokio_signal::ctrl_c().flatten_stream();
|
//!
|
||||||
|
//! #[tokio::main]
|
||||||
|
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
//! // Create an infinite stream of "Ctrl+C" notifications. Each item received
|
||||||
|
//! // on this stream may represent multiple ctrl-c signals.
|
||||||
|
//! let ctrl_c = tokio_signal::ctrl_c().await?;
|
||||||
|
//!
|
||||||
|
//! // Process each ctrl-c as it comes in
|
||||||
|
//! let prog = ctrl_c.for_each(|event| {
|
||||||
|
//! event.expect("failed to get event");
|
||||||
|
//!
|
||||||
|
//! println!("ctrl-c received!");
|
||||||
|
//! future::ready(())
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! prog.await;
|
||||||
//!
|
//!
|
||||||
//! // Process each ctrl-c as it comes in
|
|
||||||
//! let prog = ctrl_c.for_each(|()| {
|
|
||||||
//! println!("ctrl-c received!");
|
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! });
|
//! }
|
||||||
//!
|
|
||||||
//! tokio::runtime::current_thread::block_on_all(prog).unwrap();
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Wait for SIGHUP on Unix
|
//! Wait for SIGHUP on Unix
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! # #[cfg(unix)] fn dox() {
|
//! #![feature(async_await)]
|
||||||
//! use futures::{Future, Stream};
|
//!
|
||||||
|
//! use futures_util::future;
|
||||||
|
//! use futures_util::stream::StreamExt;
|
||||||
//! use tokio_signal::unix::{Signal, SIGHUP};
|
//! use tokio_signal::unix::{Signal, SIGHUP};
|
||||||
//!
|
//!
|
||||||
//! // Like the previous example, this is an infinite stream of signals
|
//! #[tokio::main]
|
||||||
//! // being received, and signals may be coalesced while pending.
|
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//! let stream = Signal::new(SIGHUP).flatten_stream();
|
//! // Create an infinite stream of "Ctrl+C" notifications. Each item received
|
||||||
|
//! // on this stream may represent multiple ctrl-c signals.
|
||||||
|
//! let ctrl_c = tokio_signal::ctrl_c().await?;
|
||||||
//!
|
//!
|
||||||
//! // Convert out stream into a future and block the program
|
//! // Process each ctrl-c as it comes in
|
||||||
//! tokio::runtime::current_thread::block_on_all(stream.into_future()).ok().unwrap();
|
//! let prog = ctrl_c.for_each(|event| {
|
||||||
//! # }
|
//! event.expect("failed to get event");
|
||||||
|
//!
|
||||||
|
//! println!("ctrl-c received!");
|
||||||
|
//! future::ready(())
|
||||||
|
//! });
|
||||||
|
//!
|
||||||
|
//! prog.await;
|
||||||
|
//!
|
||||||
|
//! // Like the previous example, this is an infinite stream of signals
|
||||||
|
//! // being received, and signals may be coalesced while pending.
|
||||||
|
//! let stream = Signal::new(SIGHUP).await?;
|
||||||
|
//!
|
||||||
|
//! // Convert out stream into a future and block the program
|
||||||
|
//! let (signal, _signal) = stream.into_future().await;
|
||||||
|
//! println!("got signal {:?}", signal);
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
use futures::stream::Stream;
|
use futures_core::future::Future;
|
||||||
use futures::{future, Future};
|
use futures_core::stream::Stream;
|
||||||
|
use futures_util::future::FutureExt;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
|
use futures_util::try_future::TryFutureExt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::pin::Pin;
|
||||||
use tokio_reactor::Handle;
|
use tokio_reactor::Handle;
|
||||||
|
|
||||||
mod registry;
|
mod registry;
|
||||||
@@ -74,10 +109,10 @@ mod os {
|
|||||||
pub mod unix;
|
pub mod unix;
|
||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
||||||
/// A future whose error is `io::Error`
|
/// A future whose output is `io::Result<T>`
|
||||||
pub type IoFuture<T> = Box<dyn Future<Item = T, Error = io::Error> + Send>;
|
pub type IoFuture<T> = Pin<Box<dyn Future<Output = io::Result<T>> + Send>>;
|
||||||
/// A stream whose error is `io::Error`
|
/// A stream whose item is `io::Result<T>`
|
||||||
pub type IoStream<T> = Box<dyn Stream<Item = T, Error = io::Error> + Send>;
|
pub type IoStream<T> = Pin<Box<dyn Stream<Item = io::Result<T>> + Send>>;
|
||||||
|
|
||||||
/// Creates a stream which receives "ctrl-c" notifications sent to a process.
|
/// Creates a stream which receives "ctrl-c" notifications sent to a process.
|
||||||
///
|
///
|
||||||
@@ -111,20 +146,15 @@ pub fn ctrl_c_handle(handle: &Handle) -> IoFuture<IoStream<()>> {
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||||
let handle = handle.clone();
|
unix::Signal::with_handle(unix::libc::SIGINT, &handle)
|
||||||
Box::new(future::lazy(move || {
|
.map_ok(|signal| -> IoStream<()> { signal.map(|_| Ok(())).boxed() })
|
||||||
unix::Signal::with_handle(unix::libc::SIGINT, &handle)
|
.boxed()
|
||||||
.map(|x| Box::new(x.map(|_| ())) as Box<dyn Stream<Item = _, Error = _> + Send>)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
fn ctrl_c_imp(handle: &Handle) -> IoFuture<IoStream<()>> {
|
||||||
let handle = handle.clone();
|
windows::Event::ctrl_c_handle(&handle)
|
||||||
// Use lazy to ensure that `ctrl_c` gets called while on an event loop
|
.map_ok(|event| -> IoStream<()> { event.map(|_| Ok(())).boxed() })
|
||||||
Box::new(future::lazy(move || {
|
.boxed()
|
||||||
windows::Event::ctrl_c_handle(&handle)
|
|
||||||
.map(|x| Box::new(x) as Box<dyn Stream<Item = _, Error = _> + Send>)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use crate::os::{OsExtraData, OsStorage};
|
use crate::os::{OsExtraData, OsStorage};
|
||||||
use futures::sync::mpsc::Sender;
|
use tokio_sync::mpsc::Sender;
|
||||||
|
|
||||||
pub(crate) type EventId = usize;
|
pub(crate) type EventId = usize;
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ impl<S: Storage> Registry<S> {
|
|||||||
for i in (0..recipients.len()).rev() {
|
for i in (0..recipients.len()).rev() {
|
||||||
match recipients[i].try_send(()) {
|
match recipients[i].try_send(()) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(ref e) if e.is_disconnected() => {
|
Err(ref e) if e.is_closed() => {
|
||||||
recipients.swap_remove(i);
|
recipients.swap_remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,65 +169,53 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use futures::sync::mpsc::channel;
|
use futures_util::{future, StreamExt};
|
||||||
use futures::sync::oneshot;
|
use tokio_sync::mpsc::channel;
|
||||||
use futures::{Future, Stream};
|
use tokio_sync::oneshot;
|
||||||
|
|
||||||
use std::time::Duration;
|
#[tokio::test]
|
||||||
use tokio::runtime::Runtime;
|
async fn smoke() {
|
||||||
use tokio_timer::sleep;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn smoke() {
|
|
||||||
let registry = Registry::new(vec![
|
let registry = Registry::new(vec![
|
||||||
EventInfo::default(),
|
EventInfo::default(),
|
||||||
EventInfo::default(),
|
EventInfo::default(),
|
||||||
EventInfo::default(),
|
EventInfo::default(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let (first_tx, first_rx) = channel(0);
|
let (first_tx, first_rx) = channel(3);
|
||||||
let (second_tx, second_rx) = channel(0);
|
let (second_tx, second_rx) = channel(3);
|
||||||
let (third_tx, third_rx) = channel(0);
|
let (third_tx, third_rx) = channel(3);
|
||||||
|
|
||||||
registry.register_listener(0, first_tx);
|
registry.register_listener(0, first_tx);
|
||||||
registry.register_listener(1, second_tx);
|
registry.register_listener(1, second_tx);
|
||||||
registry.register_listener(2, third_tx);
|
registry.register_listener(2, third_tx);
|
||||||
|
|
||||||
let (fire, wait) = oneshot::channel();
|
let (fire, wait) = oneshot::channel();
|
||||||
let rt = Runtime::new().unwrap();
|
|
||||||
|
|
||||||
rt.spawn(
|
tokio::spawn(async {
|
||||||
wait.and_then(move |_| {
|
wait.await.expect("wait failed");
|
||||||
// Record some events which should get coalesced
|
|
||||||
registry.record_event(0);
|
|
||||||
registry.record_event(0);
|
|
||||||
registry.record_event(1);
|
|
||||||
registry.record_event(1);
|
|
||||||
registry.broadcast();
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(100))
|
// Record some events which should get coalesced
|
||||||
.map_err(|e| panic!("{:#?}", e))
|
registry.record_event(0);
|
||||||
.and_then(move |_| {
|
registry.record_event(0);
|
||||||
registry.record_event(0);
|
registry.record_event(1);
|
||||||
registry.broadcast();
|
registry.record_event(1);
|
||||||
|
registry.broadcast();
|
||||||
|
|
||||||
drop(registry);
|
// Send subsequent signal
|
||||||
Ok(())
|
registry.record_event(0);
|
||||||
})
|
registry.broadcast();
|
||||||
})
|
|
||||||
.map_err(|e| panic!("{}", e)),
|
drop(registry);
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = fire.send(());
|
||||||
|
let all = future::join3(
|
||||||
|
first_rx.collect::<Vec<_>>(),
|
||||||
|
second_rx.collect::<Vec<_>>(),
|
||||||
|
third_rx.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (first_results, second_results, third_results) = rt
|
let (first_results, second_results, third_results) = all.await;
|
||||||
.block_on(futures::lazy(move || {
|
|
||||||
let _ = fire.send(());
|
|
||||||
|
|
||||||
first_rx
|
|
||||||
.collect()
|
|
||||||
.join3(second_rx.collect(), third_rx.collect())
|
|
||||||
}))
|
|
||||||
.expect("failed to extract events");
|
|
||||||
|
|
||||||
assert_eq!(2, first_results.len());
|
assert_eq!(2, first_results.len());
|
||||||
assert_eq!(1, second_results.len());
|
assert_eq!(1, second_results.len());
|
||||||
assert_eq!(0, third_results.len());
|
assert_eq!(0, third_results.len());
|
||||||
@@ -238,7 +226,7 @@ mod tests {
|
|||||||
fn register_panics_on_invalid_input() {
|
fn register_panics_on_invalid_input() {
|
||||||
let registry = Registry::new(vec![EventInfo::default()]);
|
let registry = Registry::new(vec![EventInfo::default()]);
|
||||||
|
|
||||||
let (tx, _) = channel(0);
|
let (tx, _) = channel(1);
|
||||||
registry.register_listener(1, tx);
|
registry.register_listener(1, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,13 +236,13 @@ mod tests {
|
|||||||
registry.record_event(42);
|
registry.record_event(42);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn broadcast_cleans_up_disconnected_listeners() {
|
async fn broadcast_cleans_up_disconnected_listeners() {
|
||||||
let registry = Registry::new(vec![EventInfo::default()]);
|
let registry = Registry::new(vec![EventInfo::default()]);
|
||||||
|
|
||||||
let (first_tx, first_rx) = channel(0);
|
let (first_tx, first_rx) = channel(1);
|
||||||
let (second_tx, second_rx) = channel(0);
|
let (second_tx, second_rx) = channel(1);
|
||||||
let (third_tx, third_rx) = channel(0);
|
let (third_tx, third_rx) = channel(1);
|
||||||
|
|
||||||
registry.register_listener(0, first_tx);
|
registry.register_listener(0, first_tx);
|
||||||
registry.register_listener(0, second_tx);
|
registry.register_listener(0, second_tx);
|
||||||
@@ -264,28 +252,20 @@ mod tests {
|
|||||||
drop(second_rx);
|
drop(second_rx);
|
||||||
|
|
||||||
let (fire, wait) = oneshot::channel();
|
let (fire, wait) = oneshot::channel();
|
||||||
let rt = Runtime::new().unwrap();
|
//let mut rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
rt.spawn(
|
tokio::spawn(async {
|
||||||
wait.and_then(move |_| {
|
wait.await.expect("wait failed");
|
||||||
// Record some events which should get coalesced
|
|
||||||
registry.record_event(0);
|
|
||||||
registry.broadcast();
|
|
||||||
|
|
||||||
assert_eq!(1, registry.storage[0].recipients.lock().unwrap().len());
|
registry.record_event(0);
|
||||||
drop(registry);
|
registry.broadcast();
|
||||||
|
|
||||||
Ok(())
|
assert_eq!(1, registry.storage[0].recipients.lock().unwrap().len());
|
||||||
})
|
drop(registry);
|
||||||
.map_err(|e| panic!("{}", e)),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
let results = rt
|
let _ = fire.send(());
|
||||||
.block_on(futures::lazy(move || {
|
let results: Vec<()> = third_rx.collect().await;
|
||||||
let _ = fire.send(());
|
|
||||||
third_rx.collect()
|
|
||||||
}))
|
|
||||||
.expect("failed to extract events");
|
|
||||||
|
|
||||||
assert_eq!(1, results.len());
|
assert_eq!(1, results.len());
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-47
@@ -7,20 +7,21 @@
|
|||||||
|
|
||||||
pub use libc;
|
pub use libc;
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::{self, Error, ErrorKind, Write};
|
||||||
use std::io::{self, Error, ErrorKind};
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
use futures::future;
|
use crate::IoFuture;
|
||||||
use futures::sync::mpsc::{channel, Receiver};
|
use futures_core::stream::Stream;
|
||||||
use futures::{Async, Future};
|
use futures_util::future::{self, FutureExt};
|
||||||
use futures::{Poll, Stream};
|
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
use mio_uds::UnixStream;
|
use mio_uds::UnixStream;
|
||||||
use tokio_io::IoFuture;
|
use std::future::Future;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tokio_io::AsyncRead;
|
||||||
use tokio_reactor::{Handle, PollEvented};
|
use tokio_reactor::{Handle, PollEvented};
|
||||||
|
use tokio_sync::mpsc::{channel, Receiver};
|
||||||
|
|
||||||
use crate::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
|
use crate::registry::{globals, EventId, EventInfo, Globals, Init, Storage};
|
||||||
|
|
||||||
@@ -163,17 +164,15 @@ struct Driver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Future for Driver {
|
impl Future for Driver {
|
||||||
type Item = ();
|
type Output = ();
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ()> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
// Drain the data from the pipe and maintain interest in getting more
|
// Drain the data from the pipe and maintain interest in getting more
|
||||||
self.drain();
|
self.drain(cx);
|
||||||
// Broadcast any signals which were received
|
// Broadcast any signals which were received
|
||||||
globals().broadcast();
|
globals().broadcast();
|
||||||
|
|
||||||
// This task just lives until the end of the event loop
|
Poll::Pending
|
||||||
Ok(Async::NotReady)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,13 +203,13 @@ impl Driver {
|
|||||||
/// We do *NOT* use the existence of any read bytes as evidence a sigal was
|
/// We do *NOT* use the existence of any read bytes as evidence a sigal was
|
||||||
/// received since the `pending` flags would have already been set if that
|
/// received since the `pending` flags would have already been set if that
|
||||||
/// was the case. See #38 for more info.
|
/// was the case. See #38 for more info.
|
||||||
fn drain(&mut self) {
|
fn drain(mut self: Pin<&mut Self>, cx: &mut Context<'_>) {
|
||||||
loop {
|
loop {
|
||||||
match self.wakeup.read(&mut [0; 128]) {
|
match Pin::new(&mut self.wakeup).poll_read(cx, &mut [0; 128]) {
|
||||||
Ok(0) => panic!("EOF on self-pipe"),
|
Poll::Ready(Ok(0)) => panic!("EOF on self-pipe"),
|
||||||
Ok(_) => {}
|
Poll::Ready(Ok(_)) => {}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
Poll::Ready(Err(e)) => panic!("Bad read on self-pipe: {}", e),
|
||||||
Err(e) => panic!("Bad read on self-pipe: {}", e),
|
Poll::Pending => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,44 +305,36 @@ impl Signal {
|
|||||||
/// channels will receive the signal notification.
|
/// channels will receive the signal notification.
|
||||||
pub fn with_handle(signal: c_int, handle: &Handle) -> IoFuture<Signal> {
|
pub fn with_handle(signal: c_int, handle: &Handle) -> IoFuture<Signal> {
|
||||||
let handle = handle.clone();
|
let handle = handle.clone();
|
||||||
Box::new(future::lazy(move || {
|
future::lazy(move |_| {
|
||||||
let result = (|| {
|
// Turn the signal delivery on once we are ready for it
|
||||||
// Turn the signal delivery on once we are ready for it
|
signal_enable(signal)?;
|
||||||
signal_enable(signal)?;
|
|
||||||
|
|
||||||
// Ensure there's a driver for our associated event loop processing
|
// Ensure there's a driver for our associated event loop processing
|
||||||
// signals.
|
// signals.
|
||||||
let driver = Driver::new(&handle)?;
|
let driver = Driver::new(&handle)?;
|
||||||
|
|
||||||
// One wakeup in a queue is enough, no need for us to buffer up any
|
// One wakeup in a queue is enough, no need for us to buffer up any
|
||||||
// more. NB: channels always guarantee at least one slot per sender,
|
// more.
|
||||||
// so we don't need additional slots
|
let (tx, rx) = channel(1);
|
||||||
let (tx, rx) = channel(0);
|
globals().register_listener(signal as EventId, tx);
|
||||||
globals().register_listener(signal as EventId, tx);
|
|
||||||
|
|
||||||
Ok(Signal {
|
Ok(Signal {
|
||||||
driver: driver,
|
driver: driver,
|
||||||
rx: rx,
|
rx: rx,
|
||||||
signal: signal,
|
signal: signal,
|
||||||
})
|
})
|
||||||
})();
|
})
|
||||||
future::result(result)
|
.boxed()
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream for Signal {
|
impl Stream for Signal {
|
||||||
type Item = c_int;
|
type Item = c_int;
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<c_int>, io::Error> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
self.driver.poll().unwrap();
|
let _ = Pin::new(&mut self.driver).poll(cx);
|
||||||
|
|
||||||
self.rx
|
self.rx.poll_recv(cx).map(|item| item.map(|()| self.signal))
|
||||||
.poll()
|
|
||||||
.map(|ready| ready.map(|item| item.map(|()| self.signal)))
|
|
||||||
// receivers don't generate errors
|
|
||||||
.map_err(|_| unreachable!())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
-44
@@ -8,13 +8,16 @@
|
|||||||
#![cfg(windows)]
|
#![cfg(windows)]
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use futures::future;
|
use futures_core::stream::Stream;
|
||||||
use futures::sync::mpsc::{channel, Receiver, Sender};
|
use futures_util::future::{self, FutureExt};
|
||||||
use futures::{Async, Future, Poll, Stream};
|
|
||||||
use tokio_reactor::Handle;
|
use tokio_reactor::Handle;
|
||||||
|
use tokio_sync::mpsc::{channel, Receiver, Sender};
|
||||||
use winapi::shared::minwindef::*;
|
use winapi::shared::minwindef::*;
|
||||||
use winapi::um::consoleapi::SetConsoleCtrlHandler;
|
use winapi::um::consoleapi::SetConsoleCtrlHandler;
|
||||||
use winapi::um::wincon::*;
|
use winapi::um::wincon::*;
|
||||||
@@ -62,9 +65,9 @@ pub(crate) struct OsExtraData {
|
|||||||
|
|
||||||
impl Init for OsExtraData {
|
impl Init for OsExtraData {
|
||||||
fn init() -> Self {
|
fn init() -> Self {
|
||||||
let (driver_waker, driver_rx) = channel(0);
|
let (driver_waker, driver_rx) = channel(1);
|
||||||
|
|
||||||
::tokio_executor::spawn(DriverTask { rx: driver_rx });
|
tokio_executor::spawn(DriverTask { rx: driver_rx });
|
||||||
|
|
||||||
Self { driver_waker }
|
Self { driver_waker }
|
||||||
}
|
}
|
||||||
@@ -129,7 +132,7 @@ impl Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(signum: DWORD, _handle: &Handle) -> IoFuture<Event> {
|
fn new(signum: DWORD, _handle: &Handle) -> IoFuture<Event> {
|
||||||
let new_signal = future::poll_fn(move || {
|
future::lazy(move |_| {
|
||||||
let mut init = None;
|
let mut init = None;
|
||||||
INIT.call_once(|| {
|
INIT.call_once(|| {
|
||||||
init = Some(global_init());
|
init = Some(global_init());
|
||||||
@@ -139,25 +142,20 @@ impl Event {
|
|||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = channel(0);
|
let (tx, rx) = channel(1);
|
||||||
globals().register_listener(signum as EventId, tx);
|
globals().register_listener(signum as EventId, tx);
|
||||||
|
|
||||||
Ok(Async::Ready(Event { rx }))
|
Ok(Event { rx })
|
||||||
});
|
})
|
||||||
|
.boxed()
|
||||||
Box::new(new_signal)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream for Event {
|
impl Stream for Event {
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<()>, io::Error> {
|
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
self.rx
|
self.rx.poll_recv(cx)
|
||||||
.poll()
|
|
||||||
// receivers don't generate errors
|
|
||||||
.map_err(|_| unreachable!())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,26 +171,23 @@ fn global_init() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Future for DriverTask {
|
impl Future for DriverTask {
|
||||||
type Item = ();
|
type Output = ();
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<(), ()> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
loop {
|
loop {
|
||||||
// Ensure we keep polling our waker until we know there are no more
|
// Ensure we keep polling our waker until we know there are no more
|
||||||
// events (and therefore we've registered interest to be woken again).
|
// events (and therefore we've registered interest to be woken again).
|
||||||
match self.rx.poll() {
|
match self.rx.poll_recv(cx) {
|
||||||
Ok(Async::Ready(Some(()))) => continue,
|
Poll::Ready(Some(())) => continue,
|
||||||
Ok(Async::Ready(None)) => panic!("driver got disconnected?"),
|
Poll::Ready(None) => panic!("driver got disconnected?"),
|
||||||
Ok(Async::NotReady) => break,
|
Poll::Pending => break,
|
||||||
// receivers don't generate errors
|
|
||||||
Err(()) => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
globals().broadcast();
|
globals().broadcast();
|
||||||
|
|
||||||
// TODO(1000): when to finish this task?
|
// TODO(1000): when to finish this task?
|
||||||
Ok(Async::NotReady)
|
Poll::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,20 +208,14 @@ unsafe extern "system" fn handler(ty: DWORD) -> BOOL {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use futures_util::future::FutureExt;
|
||||||
|
use futures_util::stream::StreamExt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::runtime::current_thread;
|
use tokio::runtime::current_thread;
|
||||||
use tokio::timer::Timeout;
|
use tokio_timer::Timeout;
|
||||||
|
|
||||||
fn with_timeout<F: Future>(future: F) -> impl Future<Item = F::Item, Error = F::Error> {
|
fn with_timeout<F: Future>(future: F) -> impl Future<Output = F::Output> {
|
||||||
Timeout::new(future, Duration::from_secs(1)).map_err(|e| {
|
Timeout::new(future, Duration::from_secs(1)).map(|result| result.expect("timed out"))
|
||||||
if e.is_timer() {
|
|
||||||
panic!("failed to register timer");
|
|
||||||
} else if e.is_elapsed() {
|
|
||||||
panic!("timed out")
|
|
||||||
} else {
|
|
||||||
e.into_inner().expect("missing inner error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -245,19 +234,16 @@ mod tests {
|
|||||||
super::handler(CTRL_C_EVENT);
|
super::handler(CTRL_C_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.block_on(with_timeout(event_ctrl_c.into_future()))
|
rt.block_on(with_timeout(event_ctrl_c.into_future()));
|
||||||
.ok()
|
|
||||||
.expect("failed to run event");
|
|
||||||
|
|
||||||
let event_ctrl_break = rt
|
let event_ctrl_break = rt
|
||||||
.block_on(with_timeout(Event::ctrl_break()))
|
.block_on(with_timeout(Event::ctrl_break()))
|
||||||
.expect("failed to run future");
|
.expect("failed to run future");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
super::handler(CTRL_BREAK_EVENT);
|
super::handler(CTRL_BREAK_EVENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.block_on(with_timeout(event_ctrl_break.into_future()))
|
rt.block_on(with_timeout(event_ctrl_break.into_future()));
|
||||||
.ok()
|
|
||||||
.expect("failed to run event");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,5 @@ fn dropping_loops_does_not_cause_starvation() {
|
|||||||
|
|
||||||
send_signal(TEST_SIGNAL);
|
send_signal(TEST_SIGNAL);
|
||||||
|
|
||||||
let signal_future = signal.into_future().map_err(|(e, _)| e);
|
run_with_timeout(&mut rt, signal.into_future());
|
||||||
|
|
||||||
run_with_timeout(&mut rt, signal_future).expect("failed to get signal");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
pub mod support;
|
pub mod support;
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn drop_then_get_a_signal() {
|
async fn drop_then_get_a_signal() {
|
||||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
let signal = with_timeout(Signal::new(libc::SIGUSR1))
|
||||||
let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1))
|
.await
|
||||||
.expect("failed to create first signal");
|
.expect("failed to create first signal");
|
||||||
drop(signal);
|
drop(signal);
|
||||||
|
|
||||||
send_signal(libc::SIGUSR1);
|
send_signal(libc::SIGUSR1);
|
||||||
let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1))
|
let signal = with_timeout(Signal::new(libc::SIGUSR1))
|
||||||
.expect("failed to create signal")
|
.await
|
||||||
.into_future()
|
.expect("failed to create second signal");
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|(e, _)| panic!("{}", e));
|
|
||||||
|
|
||||||
run_with_timeout(&mut rt, signal).expect("failed to get signal");
|
with_timeout(signal.into_future()).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
@@ -8,25 +9,22 @@ use crate::support::*;
|
|||||||
|
|
||||||
const TEST_SIGNAL: libc::c_int = libc::SIGUSR1;
|
const TEST_SIGNAL: libc::c_int = libc::SIGUSR1;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn dropping_signal_does_not_deregister_any_other_instances() {
|
async fn dropping_signal_does_not_deregister_any_other_instances() {
|
||||||
// NB: Deadline requires a timer registration which is provided by
|
|
||||||
// tokio's `current_thread::Runtime`, but isn't available by just using
|
|
||||||
// tokio's default CurrentThread executor which powers `current_thread::block_on_all`.
|
|
||||||
let mut rt = CurrentThreadRuntime::new().expect("failed to init runtime");
|
|
||||||
|
|
||||||
// NB: Testing for issue #38: signals should not starve based on ordering
|
// NB: Testing for issue #38: signals should not starve based on ordering
|
||||||
let first_duplicate_signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL))
|
let first_duplicate_signal = with_timeout(Signal::new(TEST_SIGNAL))
|
||||||
|
.await
|
||||||
.expect("failed to register first duplicate signal");
|
.expect("failed to register first duplicate signal");
|
||||||
let signal =
|
let signal = with_timeout(Signal::new(TEST_SIGNAL))
|
||||||
run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)).expect("failed to register signal");
|
.await
|
||||||
let second_duplicate_signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL))
|
.expect("failed to register signal");
|
||||||
|
let second_duplicate_signal = with_timeout(Signal::new(TEST_SIGNAL))
|
||||||
|
.await
|
||||||
.expect("failed to register second duplicate signal");
|
.expect("failed to register second duplicate signal");
|
||||||
|
|
||||||
drop(first_duplicate_signal);
|
drop(first_duplicate_signal);
|
||||||
drop(second_duplicate_signal);
|
drop(second_duplicate_signal);
|
||||||
|
|
||||||
send_signal(TEST_SIGNAL);
|
send_signal(TEST_SIGNAL);
|
||||||
run_with_timeout(&mut rt, signal.into_future().map_err(|(e, _)| e))
|
with_timeout(signal.into_future()).await;
|
||||||
.expect("failed to get signal");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ fn multi_loop() {
|
|||||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
let mut rt = CurrentThreadRuntime::new().unwrap();
|
||||||
let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGHUP)).unwrap();
|
let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGHUP)).unwrap();
|
||||||
sender.send(()).unwrap();
|
sender.send(()).unwrap();
|
||||||
run_with_timeout(&mut rt, signal.into_future())
|
run_with_timeout(&mut rt, signal.into_future());
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
pub mod support;
|
pub mod support;
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn notify_both() {
|
async fn notify_both() {
|
||||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
let signal1 = with_timeout(Signal::new(libc::SIGUSR2))
|
||||||
let signal1 =
|
.await
|
||||||
run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)).expect("failed to create signal1");
|
.expect("failed to create signal1");
|
||||||
|
|
||||||
let signal2 =
|
let signal2 = with_timeout(Signal::new(libc::SIGUSR2))
|
||||||
run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)).expect("failed to create signal2");
|
.await
|
||||||
|
.expect("failed to create signal2");
|
||||||
|
|
||||||
send_signal(libc::SIGUSR2);
|
send_signal(libc::SIGUSR2);
|
||||||
run_with_timeout(&mut rt, signal1.into_future().join(signal2.into_future()))
|
with_timeout(future::join(signal1.into_future(), signal2.into_future())).await;
|
||||||
.ok()
|
|
||||||
.expect("failed to receive");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#![cfg(unix)]
|
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
|
||||||
|
|
||||||
pub mod support;
|
|
||||||
use crate::support::*;
|
|
||||||
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tokio_simple() {
|
|
||||||
let signal_future = Signal::new(libc::SIGUSR1).and_then(|signal| {
|
|
||||||
send_signal(libc::SIGUSR1);
|
|
||||||
signal.into_future().map(|_| ()).map_err(|(err, _)| err)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut rt = CurrentThreadRuntime::new().expect("failed to init runtime");
|
|
||||||
run_with_timeout(&mut rt, signal_future).expect("failed");
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
pub mod support;
|
pub mod support;
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn simple() {
|
async fn simple() {
|
||||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
let signal = with_timeout(Signal::new(libc::SIGUSR1))
|
||||||
let signal =
|
.await
|
||||||
run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)).expect("failed to create signal");
|
.expect("failed to create signal");
|
||||||
|
|
||||||
send_signal(libc::SIGUSR1);
|
send_signal(libc::SIGUSR1);
|
||||||
|
|
||||||
run_with_timeout(&mut rt, signal.into_future())
|
with_timeout(signal.into_future()).await;
|
||||||
.ok()
|
|
||||||
.expect("failed to get signal");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
|
||||||
|
use futures_util::future::FutureExt;
|
||||||
use libc::{c_int, getpid, kill};
|
use libc::{c_int, getpid, kill};
|
||||||
|
use std::future::Future;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::timer::Timeout;
|
use tokio_timer::Timeout;
|
||||||
|
|
||||||
pub use futures::{Future, Stream};
|
pub use futures_util::future;
|
||||||
|
pub use futures_util::stream::StreamExt;
|
||||||
pub use tokio::runtime::current_thread::{self, Runtime as CurrentThreadRuntime};
|
pub use tokio::runtime::current_thread::{self, Runtime as CurrentThreadRuntime};
|
||||||
pub use tokio_signal::unix::Signal;
|
pub use tokio_signal::unix::Signal;
|
||||||
|
|
||||||
pub fn with_timeout<F: Future>(future: F) -> impl Future<Item = F::Item, Error = F::Error> {
|
pub fn with_timeout<F: Future>(future: F) -> impl Future<Output = F::Output> {
|
||||||
Timeout::new(future, Duration::from_secs(1)).map_err(|e| {
|
Timeout::new(future, Duration::from_secs(1)).map(Result::unwrap)
|
||||||
if e.is_timer() {
|
|
||||||
panic!("failed to register timer");
|
|
||||||
} else if e.is_elapsed() {
|
|
||||||
panic!("timed out")
|
|
||||||
} else {
|
|
||||||
e.into_inner().expect("missing inner error")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_timeout<F>(rt: &mut CurrentThreadRuntime, future: F) -> Result<F::Item, F::Error>
|
pub fn run_with_timeout<F>(rt: &mut CurrentThreadRuntime, future: F) -> F::Output
|
||||||
where
|
where
|
||||||
F: Future,
|
F: Future,
|
||||||
{
|
{
|
||||||
|
|||||||
+13
-13
@@ -1,24 +1,24 @@
|
|||||||
#![cfg(unix)]
|
#![cfg(unix)]
|
||||||
#![deny(warnings, rust_2018_idioms)]
|
#![deny(warnings, rust_2018_idioms)]
|
||||||
|
#![feature(async_await)]
|
||||||
|
|
||||||
pub mod support;
|
pub mod support;
|
||||||
use crate::support::*;
|
use crate::support::*;
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn twice() {
|
async fn twice() {
|
||||||
let mut rt = CurrentThreadRuntime::new().unwrap();
|
let mut signal = with_timeout(Signal::new(libc::SIGUSR1))
|
||||||
let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)).unwrap();
|
.await
|
||||||
|
.expect("failed to get signal");
|
||||||
|
|
||||||
send_signal(libc::SIGUSR1);
|
for _ in 0..2 {
|
||||||
let (num, signal) = run_with_timeout(&mut rt, signal.into_future())
|
send_signal(libc::SIGUSR1);
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(num, Some(libc::SIGUSR1));
|
|
||||||
|
|
||||||
send_signal(libc::SIGUSR1);
|
let (num, sig) = with_timeout(signal.into_future()).await;
|
||||||
run_with_timeout(&mut rt, signal.into_future())
|
assert_eq!(num, Some(libc::SIGUSR1));
|
||||||
.ok()
|
|
||||||
.unwrap();
|
signal = sig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user