diff --git a/Cargo.toml b/Cargo.toml index de01ba670..25e84bd1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ "tokio-io", "tokio-macros", "tokio-reactor", - # "tokio-signal", + "tokio-signal", "tokio-sync", "tokio-test", "tokio-threadpool", diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9b9d3b187..35517b287 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -32,7 +32,7 @@ jobs: crates: # - tokio-fs tokio-reactor: [] -# - tokio-signal + tokio-signal: [] tokio-tcp: - incoming # - tokio-tls diff --git a/tokio-signal/Cargo.toml b/tokio-signal/Cargo.toml index 62c250bc1..4829f8687 100644 --- a/tokio-signal/Cargo.toml +++ b/tokio-signal/Cargo.toml @@ -26,11 +26,13 @@ travis-ci = { repository = "tokio-rs/tokio" } appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" } [dependencies] -futures = "0.1.11" +futures-core-preview = "0.3.0-alpha.16" +futures-util-preview = "0.3.0-alpha.16" lazy_static = "1" tokio-reactor = { version = "0.2.0", path = "../tokio-reactor" } tokio-executor = { version = "0.2.0", path = "../tokio-executor" } tokio-io = { version = "0.2.0", path = "../tokio-io" } +tokio-sync = { version = "0.2.0", path = "../tokio-sync" } [target.'cfg(unix)'.dependencies] libc = "0.2" @@ -41,6 +43,7 @@ signal-hook-registry = "~1" [dev-dependencies] tokio = { version = "0.2.0", path = "../tokio" } 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] version = "0.3" diff --git a/tokio-signal/examples/ctrl-c.rs b/tokio-signal/examples/ctrl-c.rs index a05e26254..b3032b97f 100644 --- a/tokio-signal/examples/ctrl-c.rs +++ b/tokio-signal/examples/ctrl-c.rs @@ -1,22 +1,20 @@ #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] -use tokio; -use tokio_signal; - -use futures::{Future, Stream}; +use futures_util::future; +use futures_util::stream::StreamExt; /// how many signals to handle before exiting const STOP_AFTER: u64 = 10; -fn main() -> Result<(), Box> { +#[tokio::main] +async fn main() -> Result<(), Box> { // tokio_signal provides a convenience builder for Ctrl+C // this even works cross-platform: linux and windows! // // `fn ctrl_c()` produces a `Future` of the actual stream-initialisation - // the `flatten_stream()` convenience method lazily defers that - // initialisation, allowing us to use it 'as if' it is already the - // stream we want, reducing boilerplate Future-handling. - let endless_stream = tokio_signal::ctrl_c().flatten_stream(); + // so first we await until the signal is ready. + let endless_stream = tokio_signal::ctrl_c().await?; // don't keep going forever: convert the endless stream to a bounded one. let limited_stream = endless_stream.take(STOP_AFTER); @@ -36,30 +34,26 @@ fn main() -> Result<(), Box> { // 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 // have been completed, or the first time the closure returns an error - let future = limited_stream.for_each(|()| { - // Note how we manipulate the counter without any fancy synchronisation. - // The borrowchecker realises there can't be any conflicts, so the closure - // can just capture it. - counter += 1; - println!( - "Ctrl+C received {} times! {} more before exit", - counter, - STOP_AFTER - counter - ); + let future = limited_stream + .map(|result| result.expect("failed to get event")) + .for_each(|()| { + // Note how we manipulate the counter without any fancy synchronisation. + // The borrowchecker realises there can't be any conflicts, so the closure + // can just capture it. + counter += 1; + println!( + "Ctrl+C received {} times! {} more before exit", + counter, + STOP_AFTER - counter + ); - // return Ok-result to continue handling the stream - Ok(()) - }); + // return a result to continue handling the stream + future::ready(()) + }); // Up until now, we haven't really DONE anything, just prepared - // now it's time to actually schedule, and thus execute, the stream - // on our event loop - // 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)?; + // now it's time to actually the results! + future.await; println!("Stream ended, quiting the program."); Ok(()) diff --git a/tokio-signal/examples/multiple.rs b/tokio-signal/examples/multiple.rs index c0443ce7e..6a640a84e 100644 --- a/tokio-signal/examples/multiple.rs +++ b/tokio-signal/examples/multiple.rs @@ -1,21 +1,25 @@ #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] //! 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. #[cfg(unix)] mod platform { - use futures::{Future, Stream}; + use futures_util::stream::{self, StreamExt}; + use std::error::Error; use tokio_signal::unix::{Signal, SIGINT, SIGTERM}; - pub fn main() -> Result<(), Box> { + pub async fn main() -> Result<(), Box> { // Create a stream for each of the signals we'd like to handle. - let sigint = Signal::new(SIGINT).flatten_stream(); - let sigterm = Signal::new(SIGTERM).flatten_stream(); + let sigint = Signal::new(SIGINT).await?; + let sigterm = Signal::new(SIGTERM).await?; // 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 println!("Waiting for SIGINT or SIGTERM"); @@ -24,8 +28,7 @@ mod platform { to send a SIGINT to all processes named 'multiple' \ (i.e. this binary)" ); - let (item, _rest) = ::tokio::runtime::current_thread::block_on_all(stream.into_future()) - .map_err(|_| "failed to wait for signals")?; + let (item, _rest) = stream.into_future().await; // Figure out which signal we received let item = item.ok_or("received no signal")?; @@ -35,6 +38,7 @@ mod platform { assert_eq!(item, SIGTERM); println!("received SIGTERM"); } + Ok(()) } @@ -42,11 +46,13 @@ mod platform { #[cfg(not(unix))] mod platform { - pub fn main() -> Result<(), Box> { + use std::error::Error; + pub async fn main() -> Result<(), Box> { Ok(()) } } -fn main() -> Result<(), Box> { - platform::main() +#[tokio::main] +async fn main() -> Result<(), Box> { + platform::main().await } diff --git a/tokio-signal/examples/sighup-example.rs b/tokio-signal/examples/sighup-example.rs index 983469121..a5b00eac7 100644 --- a/tokio-signal/examples/sighup-example.rs +++ b/tokio-signal/examples/sighup-example.rs @@ -1,15 +1,20 @@ #![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. #[cfg(unix)] 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}; - pub fn main() -> Result<(), Box> { + pub async fn main() -> Result<(), Box> { // 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!( @@ -27,25 +32,27 @@ mod platform { or something", the_signal ); - Ok(()) + + future::ready(()) }); // Up until now, we haven't really DONE anything, just prepared - // now it's time to actually schedule, and thus execute, the stream - // on our event loop, and loop forever - ::tokio::runtime::current_thread::block_on_all(future)?; + // now it's time to actually the results! + future.await; + Ok(()) } - } #[cfg(not(unix))] mod platform { - pub fn main() -> Result<(), Box> { + use std::error::Error; + pub async fn main() -> Result<(), Box> { Ok(()) } } -fn main() -> Result<(), Box> { - platform::main() +#[tokio::main] +async fn main() -> Result<(), Box> { + platform::main().await } diff --git a/tokio-signal/src/lib.rs b/tokio-signal/src/lib.rs index dea33afc1..03082dab8 100644 --- a/tokio-signal/src/lib.rs +++ b/tokio-signal/src/lib.rs @@ -1,6 +1,7 @@ #![doc(html_root_url = "https://docs.rs/tokio-signal/0.2.8")] #![deny(missing_docs, rust_2018_idioms)] #![cfg_attr(test, deny(warnings))] +#![cfg_attr(test, feature(async_await))] #![doc(test(no_crate_inject, attr(deny(rust_2018_idioms))))] //! Asynchronous signal handling for Tokio @@ -23,43 +24,77 @@ //! Print out all ctrl-C notifications received //! //! ```rust,no_run -//! use futures::{Future, Stream}; +//! #![feature(async_await)] //! -//! // 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().flatten_stream(); +//! use futures_util::future; +//! use futures_util::stream::StreamExt; +//! +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! // 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(()) -//! }); -//! -//! tokio::runtime::current_thread::block_on_all(prog).unwrap(); +//! } //! ``` //! //! Wait for SIGHUP on Unix //! //! ```rust,no_run -//! # #[cfg(unix)] fn dox() { -//! use futures::{Future, Stream}; +//! #![feature(async_await)] +//! +//! use futures_util::future; +//! use futures_util::stream::StreamExt; //! use tokio_signal::unix::{Signal, SIGHUP}; //! -//! // 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).flatten_stream(); +//! #[tokio::main] +//! async fn main() -> Result<(), Box> { +//! // 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 -//! tokio::runtime::current_thread::block_on_all(stream.into_future()).ok().unwrap(); -//! # } +//! // 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; +//! +//! // 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] extern crate lazy_static; -use futures::stream::Stream; -use futures::{future, Future}; +use futures_core::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::pin::Pin; use tokio_reactor::Handle; mod registry; @@ -74,10 +109,10 @@ mod os { pub mod unix; pub mod windows; -/// A future whose error is `io::Error` -pub type IoFuture = Box + Send>; -/// A stream whose error is `io::Error` -pub type IoStream = Box + Send>; +/// A future whose output is `io::Result` +pub type IoFuture = Pin> + Send>>; +/// A stream whose item is `io::Result` +pub type IoStream = Pin> + Send>>; /// Creates a stream which receives "ctrl-c" notifications sent to a process. /// @@ -111,20 +146,15 @@ pub fn ctrl_c_handle(handle: &Handle) -> IoFuture> { #[cfg(unix)] fn ctrl_c_imp(handle: &Handle) -> IoFuture> { - let handle = handle.clone(); - Box::new(future::lazy(move || { - unix::Signal::with_handle(unix::libc::SIGINT, &handle) - .map(|x| Box::new(x.map(|_| ())) as Box + Send>) - })) + unix::Signal::with_handle(unix::libc::SIGINT, &handle) + .map_ok(|signal| -> IoStream<()> { signal.map(|_| Ok(())).boxed() }) + .boxed() } #[cfg(windows)] fn ctrl_c_imp(handle: &Handle) -> IoFuture> { - let handle = handle.clone(); - // Use lazy to ensure that `ctrl_c` gets called while on an event loop - Box::new(future::lazy(move || { - windows::Event::ctrl_c_handle(&handle) - .map(|x| Box::new(x) as Box + Send>) - })) + windows::Event::ctrl_c_handle(&handle) + .map_ok(|event| -> IoStream<()> { event.map(|_| Ok(())).boxed() }) + .boxed() } } diff --git a/tokio-signal/src/registry.rs b/tokio-signal/src/registry.rs index cf6705a92..cfeda99ea 100644 --- a/tokio-signal/src/registry.rs +++ b/tokio-signal/src/registry.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use crate::os::{OsExtraData, OsStorage}; -use futures::sync::mpsc::Sender; +use tokio_sync::mpsc::Sender; pub(crate) type EventId = usize; @@ -98,7 +98,7 @@ impl Registry { for i in (0..recipients.len()).rev() { match recipients[i].try_send(()) { Ok(()) => {} - Err(ref e) if e.is_disconnected() => { + Err(ref e) if e.is_closed() => { recipients.swap_remove(i); } @@ -169,65 +169,53 @@ where #[cfg(test)] mod tests { use super::*; - use futures::sync::mpsc::channel; - use futures::sync::oneshot; - use futures::{Future, Stream}; + use futures_util::{future, StreamExt}; + use tokio_sync::mpsc::channel; + use tokio_sync::oneshot; - use std::time::Duration; - use tokio::runtime::Runtime; - use tokio_timer::sleep; - - #[test] - fn smoke() { + #[tokio::test] + async fn smoke() { let registry = Registry::new(vec![ EventInfo::default(), EventInfo::default(), EventInfo::default(), ]); - let (first_tx, first_rx) = channel(0); - let (second_tx, second_rx) = channel(0); - let (third_tx, third_rx) = channel(0); + let (first_tx, first_rx) = channel(3); + let (second_tx, second_rx) = channel(3); + let (third_tx, third_rx) = channel(3); registry.register_listener(0, first_tx); registry.register_listener(1, second_tx); registry.register_listener(2, third_tx); let (fire, wait) = oneshot::channel(); - let rt = Runtime::new().unwrap(); - rt.spawn( - wait.and_then(move |_| { - // 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(); + tokio::spawn(async { + wait.await.expect("wait failed"); - sleep(Duration::from_millis(100)) - .map_err(|e| panic!("{:#?}", e)) - .and_then(move |_| { - registry.record_event(0); - registry.broadcast(); + // 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(); - drop(registry); - Ok(()) - }) - }) - .map_err(|e| panic!("{}", e)), + // Send subsequent signal + registry.record_event(0); + registry.broadcast(); + + drop(registry); + }); + + let _ = fire.send(()); + let all = future::join3( + first_rx.collect::>(), + second_rx.collect::>(), + third_rx.collect::>(), ); - let (first_results, second_results, third_results) = rt - .block_on(futures::lazy(move || { - let _ = fire.send(()); - - first_rx - .collect() - .join3(second_rx.collect(), third_rx.collect()) - })) - .expect("failed to extract events"); - + let (first_results, second_results, third_results) = all.await; assert_eq!(2, first_results.len()); assert_eq!(1, second_results.len()); assert_eq!(0, third_results.len()); @@ -238,7 +226,7 @@ mod tests { fn register_panics_on_invalid_input() { let registry = Registry::new(vec![EventInfo::default()]); - let (tx, _) = channel(0); + let (tx, _) = channel(1); registry.register_listener(1, tx); } @@ -248,13 +236,13 @@ mod tests { registry.record_event(42); } - #[test] - fn broadcast_cleans_up_disconnected_listeners() { + #[tokio::test] + async fn broadcast_cleans_up_disconnected_listeners() { let registry = Registry::new(vec![EventInfo::default()]); - let (first_tx, first_rx) = channel(0); - let (second_tx, second_rx) = channel(0); - let (third_tx, third_rx) = channel(0); + let (first_tx, first_rx) = channel(1); + let (second_tx, second_rx) = channel(1); + let (third_tx, third_rx) = channel(1); registry.register_listener(0, first_tx); registry.register_listener(0, second_tx); @@ -264,28 +252,20 @@ mod tests { drop(second_rx); let (fire, wait) = oneshot::channel(); - let rt = Runtime::new().unwrap(); + //let mut rt = Runtime::new().unwrap(); - rt.spawn( - wait.and_then(move |_| { - // Record some events which should get coalesced - registry.record_event(0); - registry.broadcast(); + tokio::spawn(async { + wait.await.expect("wait failed"); - assert_eq!(1, registry.storage[0].recipients.lock().unwrap().len()); - drop(registry); + registry.record_event(0); + registry.broadcast(); - Ok(()) - }) - .map_err(|e| panic!("{}", e)), - ); + assert_eq!(1, registry.storage[0].recipients.lock().unwrap().len()); + drop(registry); + }); - let results = rt - .block_on(futures::lazy(move || { - let _ = fire.send(()); - third_rx.collect() - })) - .expect("failed to extract events"); + let _ = fire.send(()); + let results: Vec<()> = third_rx.collect().await; assert_eq!(1, results.len()); } diff --git a/tokio-signal/src/unix.rs b/tokio-signal/src/unix.rs index 4dd06aeb2..9c07c443f 100644 --- a/tokio-signal/src/unix.rs +++ b/tokio-signal/src/unix.rs @@ -7,20 +7,21 @@ pub use libc; -use std::io::prelude::*; -use std::io::{self, Error, ErrorKind}; +use std::io::{self, Error, ErrorKind, Write}; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Once, ONCE_INIT}; -use futures::future; -use futures::sync::mpsc::{channel, Receiver}; -use futures::{Async, Future}; -use futures::{Poll, Stream}; +use crate::IoFuture; +use futures_core::stream::Stream; +use futures_util::future::{self, FutureExt}; use libc::c_int; 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_sync::mpsc::{channel, Receiver}; use crate::registry::{globals, EventId, EventInfo, Globals, Init, Storage}; @@ -163,17 +164,15 @@ struct Driver { } impl Future for Driver { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // Drain the data from the pipe and maintain interest in getting more - self.drain(); + self.drain(cx); // Broadcast any signals which were received globals().broadcast(); - // This task just lives until the end of the event loop - Ok(Async::NotReady) + Poll::Pending } } @@ -204,13 +203,13 @@ impl Driver { /// 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 /// was the case. See #38 for more info. - fn drain(&mut self) { + fn drain(mut self: Pin<&mut Self>, cx: &mut Context<'_>) { loop { - match self.wakeup.read(&mut [0; 128]) { - Ok(0) => panic!("EOF on self-pipe"), - Ok(_) => {} - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break, - Err(e) => panic!("Bad read on self-pipe: {}", e), + match Pin::new(&mut self.wakeup).poll_read(cx, &mut [0; 128]) { + Poll::Ready(Ok(0)) => panic!("EOF on self-pipe"), + Poll::Ready(Ok(_)) => {} + Poll::Ready(Err(e)) => panic!("Bad read on self-pipe: {}", e), + Poll::Pending => break, } } } @@ -306,44 +305,36 @@ impl Signal { /// channels will receive the signal notification. pub fn with_handle(signal: c_int, handle: &Handle) -> IoFuture { let handle = handle.clone(); - Box::new(future::lazy(move || { - let result = (|| { - // Turn the signal delivery on once we are ready for it - signal_enable(signal)?; + future::lazy(move |_| { + // Turn the signal delivery on once we are ready for it + signal_enable(signal)?; - // Ensure there's a driver for our associated event loop processing - // signals. - let driver = Driver::new(&handle)?; + // Ensure there's a driver for our associated event loop processing + // signals. + let driver = Driver::new(&handle)?; - // 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, - // so we don't need additional slots - let (tx, rx) = channel(0); - globals().register_listener(signal as EventId, tx); + // One wakeup in a queue is enough, no need for us to buffer up any + // more. + let (tx, rx) = channel(1); + globals().register_listener(signal as EventId, tx); - Ok(Signal { - driver: driver, - rx: rx, - signal: signal, - }) - })(); - future::result(result) - })) + Ok(Signal { + driver: driver, + rx: rx, + signal: signal, + }) + }) + .boxed() } } impl Stream for Signal { type Item = c_int; - type Error = io::Error; - fn poll(&mut self) -> Poll, io::Error> { - self.driver.poll().unwrap(); + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let _ = Pin::new(&mut self.driver).poll(cx); - self.rx - .poll() - .map(|ready| ready.map(|item| item.map(|()| self.signal))) - // receivers don't generate errors - .map_err(|_| unreachable!()) + self.rx.poll_recv(cx).map(|item| item.map(|()| self.signal)) } } diff --git a/tokio-signal/src/windows.rs b/tokio-signal/src/windows.rs index f42854a02..784a8243d 100644 --- a/tokio-signal/src/windows.rs +++ b/tokio-signal/src/windows.rs @@ -8,13 +8,16 @@ #![cfg(windows)] use std::convert::TryFrom; +use std::future::Future; use std::io; +use std::pin::Pin; use std::sync::{Once, ONCE_INIT}; +use std::task::{Context, Poll}; -use futures::future; -use futures::sync::mpsc::{channel, Receiver, Sender}; -use futures::{Async, Future, Poll, Stream}; +use futures_core::stream::Stream; +use futures_util::future::{self, FutureExt}; use tokio_reactor::Handle; +use tokio_sync::mpsc::{channel, Receiver, Sender}; use winapi::shared::minwindef::*; use winapi::um::consoleapi::SetConsoleCtrlHandler; use winapi::um::wincon::*; @@ -62,9 +65,9 @@ pub(crate) struct OsExtraData { impl Init for OsExtraData { 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 } } @@ -129,7 +132,7 @@ impl Event { } fn new(signum: DWORD, _handle: &Handle) -> IoFuture { - let new_signal = future::poll_fn(move || { + future::lazy(move |_| { let mut init = None; INIT.call_once(|| { init = Some(global_init()); @@ -139,25 +142,20 @@ impl Event { return Err(e); } - let (tx, rx) = channel(0); + let (tx, rx) = channel(1); globals().register_listener(signum as EventId, tx); - Ok(Async::Ready(Event { rx })) - }); - - Box::new(new_signal) + Ok(Event { rx }) + }) + .boxed() } } impl Stream for Event { type Item = (); - type Error = io::Error; - fn poll(&mut self) -> Poll, io::Error> { - self.rx - .poll() - // receivers don't generate errors - .map_err(|_| unreachable!()) + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.rx.poll_recv(cx) } } @@ -173,26 +171,23 @@ fn global_init() -> io::Result<()> { } impl Future for DriverTask { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { // Ensure we keep polling our waker until we know there are no more // events (and therefore we've registered interest to be woken again). - match self.rx.poll() { - Ok(Async::Ready(Some(()))) => continue, - Ok(Async::Ready(None)) => panic!("driver got disconnected?"), - Ok(Async::NotReady) => break, - // receivers don't generate errors - Err(()) => unreachable!(), + match self.rx.poll_recv(cx) { + Poll::Ready(Some(())) => continue, + Poll::Ready(None) => panic!("driver got disconnected?"), + Poll::Pending => break, } } globals().broadcast(); // 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)] mod tests { use super::*; + use futures_util::future::FutureExt; + use futures_util::stream::StreamExt; use std::time::Duration; use tokio::runtime::current_thread; - use tokio::timer::Timeout; + use tokio_timer::Timeout; - fn with_timeout(future: F) -> impl Future { - Timeout::new(future, Duration::from_secs(1)).map_err(|e| { - 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") - } - }) + fn with_timeout(future: F) -> impl Future { + Timeout::new(future, Duration::from_secs(1)).map(|result| result.expect("timed out")) } #[test] @@ -245,19 +234,16 @@ mod tests { super::handler(CTRL_C_EVENT); } - rt.block_on(with_timeout(event_ctrl_c.into_future())) - .ok() - .expect("failed to run event"); + rt.block_on(with_timeout(event_ctrl_c.into_future())); let event_ctrl_break = rt .block_on(with_timeout(Event::ctrl_break())) .expect("failed to run future"); + unsafe { super::handler(CTRL_BREAK_EVENT); } - rt.block_on(with_timeout(event_ctrl_break.into_future())) - .ok() - .expect("failed to run event"); + rt.block_on(with_timeout(event_ctrl_break.into_future())); } } diff --git a/tokio-signal/tests/drop_multi_loop.rs b/tokio-signal/tests/drop_multi_loop.rs index bc8214a50..f6fb606d6 100644 --- a/tokio-signal/tests/drop_multi_loop.rs +++ b/tokio-signal/tests/drop_multi_loop.rs @@ -29,7 +29,5 @@ fn dropping_loops_does_not_cause_starvation() { send_signal(TEST_SIGNAL); - let signal_future = signal.into_future().map_err(|(e, _)| e); - - run_with_timeout(&mut rt, signal_future).expect("failed to get signal"); + run_with_timeout(&mut rt, signal.into_future()); } diff --git a/tokio-signal/tests/drop_then_get_a_signal.rs b/tokio-signal/tests/drop_then_get_a_signal.rs index 4530860c1..ae5e626b7 100644 --- a/tokio-signal/tests/drop_then_get_a_signal.rs +++ b/tokio-signal/tests/drop_then_get_a_signal.rs @@ -1,24 +1,23 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] use libc; pub mod support; use crate::support::*; -#[test] -fn drop_then_get_a_signal() { - let mut rt = CurrentThreadRuntime::new().unwrap(); - let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)) +#[tokio::test] +async fn drop_then_get_a_signal() { + let signal = with_timeout(Signal::new(libc::SIGUSR1)) + .await .expect("failed to create first signal"); drop(signal); send_signal(libc::SIGUSR1); - let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)) - .expect("failed to create signal") - .into_future() - .map(|_| ()) - .map_err(|(e, _)| panic!("{}", e)); + let signal = with_timeout(Signal::new(libc::SIGUSR1)) + .await + .expect("failed to create second signal"); - run_with_timeout(&mut rt, signal).expect("failed to get signal"); + with_timeout(signal.into_future()).await; } diff --git a/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs b/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs index 39741d29d..d44a5d946 100644 --- a/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs +++ b/tokio-signal/tests/dropping_does_not_deregister_other_instances.rs @@ -1,5 +1,6 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] use libc; @@ -8,25 +9,22 @@ use crate::support::*; const TEST_SIGNAL: libc::c_int = libc::SIGUSR1; -#[test] -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"); - +#[tokio::test] +async fn dropping_signal_does_not_deregister_any_other_instances() { // 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"); - let signal = - run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)).expect("failed to register signal"); - let second_duplicate_signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)) + let signal = with_timeout(Signal::new(TEST_SIGNAL)) + .await + .expect("failed to register signal"); + let second_duplicate_signal = with_timeout(Signal::new(TEST_SIGNAL)) + .await .expect("failed to register second duplicate signal"); drop(first_duplicate_signal); drop(second_duplicate_signal); send_signal(TEST_SIGNAL); - run_with_timeout(&mut rt, signal.into_future().map_err(|(e, _)| e)) - .expect("failed to get signal"); + with_timeout(signal.into_future()).await; } diff --git a/tokio-signal/tests/multi_loop.rs b/tokio-signal/tests/multi_loop.rs index 112139f88..afcb2f77a 100644 --- a/tokio-signal/tests/multi_loop.rs +++ b/tokio-signal/tests/multi_loop.rs @@ -22,9 +22,7 @@ fn multi_loop() { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGHUP)).unwrap(); sender.send(()).unwrap(); - run_with_timeout(&mut rt, signal.into_future()) - .ok() - .unwrap(); + run_with_timeout(&mut rt, signal.into_future()); }) }) .collect(); diff --git a/tokio-signal/tests/notify_both.rs b/tokio-signal/tests/notify_both.rs index 16f73d610..0b1526a38 100644 --- a/tokio-signal/tests/notify_both.rs +++ b/tokio-signal/tests/notify_both.rs @@ -1,22 +1,22 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] pub mod support; use crate::support::*; use libc; -#[test] -fn notify_both() { - let mut rt = CurrentThreadRuntime::new().unwrap(); - let signal1 = - run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)).expect("failed to create signal1"); +#[tokio::test] +async fn notify_both() { + let signal1 = with_timeout(Signal::new(libc::SIGUSR2)) + .await + .expect("failed to create signal1"); - let signal2 = - run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)).expect("failed to create signal2"); + let signal2 = with_timeout(Signal::new(libc::SIGUSR2)) + .await + .expect("failed to create signal2"); send_signal(libc::SIGUSR2); - run_with_timeout(&mut rt, signal1.into_future().join(signal2.into_future())) - .ok() - .expect("failed to receive"); + with_timeout(future::join(signal1.into_future(), signal2.into_future())).await; } diff --git a/tokio-signal/tests/signal.rs b/tokio-signal/tests/signal.rs deleted file mode 100644 index a771e53f4..000000000 --- a/tokio-signal/tests/signal.rs +++ /dev/null @@ -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"); -} diff --git a/tokio-signal/tests/simple.rs b/tokio-signal/tests/simple.rs index 55a14cbd0..29e528981 100644 --- a/tokio-signal/tests/simple.rs +++ b/tokio-signal/tests/simple.rs @@ -1,20 +1,19 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] pub mod support; use crate::support::*; use libc; -#[test] -fn simple() { - let mut rt = CurrentThreadRuntime::new().unwrap(); - let signal = - run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)).expect("failed to create signal"); +#[tokio::test] +async fn simple() { + let signal = with_timeout(Signal::new(libc::SIGUSR1)) + .await + .expect("failed to create signal"); send_signal(libc::SIGUSR1); - run_with_timeout(&mut rt, signal.into_future()) - .ok() - .expect("failed to get signal"); + with_timeout(signal.into_future()).await; } diff --git a/tokio-signal/tests/support.rs b/tokio-signal/tests/support.rs index f7644d665..a7d4204b8 100644 --- a/tokio-signal/tests/support.rs +++ b/tokio-signal/tests/support.rs @@ -1,27 +1,22 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +use futures_util::future::FutureExt; use libc::{c_int, getpid, kill}; +use std::future::Future; 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_signal::unix::Signal; -pub fn with_timeout(future: F) -> impl Future { - Timeout::new(future, Duration::from_secs(1)).map_err(|e| { - 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 with_timeout(future: F) -> impl Future { + Timeout::new(future, Duration::from_secs(1)).map(Result::unwrap) } -pub fn run_with_timeout(rt: &mut CurrentThreadRuntime, future: F) -> Result +pub fn run_with_timeout(rt: &mut CurrentThreadRuntime, future: F) -> F::Output where F: Future, { diff --git a/tokio-signal/tests/twice.rs b/tokio-signal/tests/twice.rs index e2a450087..75aa8f8a5 100644 --- a/tokio-signal/tests/twice.rs +++ b/tokio-signal/tests/twice.rs @@ -1,24 +1,24 @@ #![cfg(unix)] #![deny(warnings, rust_2018_idioms)] +#![feature(async_await)] pub mod support; use crate::support::*; use libc; -#[test] -fn twice() { - let mut rt = CurrentThreadRuntime::new().unwrap(); - let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)).unwrap(); +#[tokio::test] +async fn twice() { + let mut signal = with_timeout(Signal::new(libc::SIGUSR1)) + .await + .expect("failed to get signal"); - send_signal(libc::SIGUSR1); - let (num, signal) = run_with_timeout(&mut rt, signal.into_future()) - .ok() - .unwrap(); - assert_eq!(num, Some(libc::SIGUSR1)); + for _ in 0..2 { + send_signal(libc::SIGUSR1); - send_signal(libc::SIGUSR1); - run_with_timeout(&mut rt, signal.into_future()) - .ok() - .unwrap(); + let (num, sig) = with_timeout(signal.into_future()).await; + assert_eq!(num, Some(libc::SIGUSR1)); + + signal = sig; + } }