From 0ee0b9379882b8cc91bf6c4e3de89e3596fc6860 Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:15:40 +0200 Subject: [PATCH 01/10] Upgrade ctrl+c example into `cargo run`-able version with proper Cargo.toml --- examples/ctrl-c/Cargo.toml | 9 +++++++++ examples/{log.rs => ctrl-c/src/main.rs} | 0 2 files changed, 9 insertions(+) create mode 100644 examples/ctrl-c/Cargo.toml rename examples/{log.rs => ctrl-c/src/main.rs} (100%) diff --git a/examples/ctrl-c/Cargo.toml b/examples/ctrl-c/Cargo.toml new file mode 100644 index 000000000..80716c381 --- /dev/null +++ b/examples/ctrl-c/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ctrl-c" +version = "0.1.0" +authors = ["Jules Kerssemakers "] + +[dependencies] +futures = "0.1.14" +tokio-core = "0.1.7" +tokio-signal = "0.1" diff --git a/examples/log.rs b/examples/ctrl-c/src/main.rs similarity index 100% rename from examples/log.rs rename to examples/ctrl-c/src/main.rs From a7efdd051b26e7ef70b66eb20a8b0935395496ed Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:18:26 +0200 Subject: [PATCH 02/10] ctrl+C example: Prompt user to do something. So we don't stay at a blank terminal without any feedback after `cargo run` --- examples/ctrl-c/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/ctrl-c/src/main.rs b/examples/ctrl-c/src/main.rs index 6d147362a..3dadad73e 100644 --- a/examples/ctrl-c/src/main.rs +++ b/examples/ctrl-c/src/main.rs @@ -10,6 +10,8 @@ fn main() { let ctrlc = tokio_signal::ctrl_c(&core.handle()); let stream = core.run(ctrlc).unwrap(); + println!("This program is now waiting for you to press Ctrl+C"); + core.run(stream.for_each(|()| { println!("Ctrl-C received!"); Ok(()) From 7dc9818dcad17231967190cd1787de0055cf4b38 Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:20:25 +0200 Subject: [PATCH 03/10] Ctrl+C example: add explanatory comments --- examples/ctrl-c/src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/ctrl-c/src/main.rs b/examples/ctrl-c/src/main.rs index 3dadad73e..cee695806 100644 --- a/examples/ctrl-c/src/main.rs +++ b/examples/ctrl-c/src/main.rs @@ -6,12 +6,19 @@ use futures::stream::Stream; use tokio_core::reactor::Core; fn main() { + // set up a Tokio event loop let mut core = Core::new().unwrap(); + + // tokio_signal provides a convenience builder for Ctrl+C + // this even works cross-platform, linux and windows! let ctrlc = tokio_signal::ctrl_c(&core.handle()); let stream = core.run(ctrlc).unwrap(); println!("This program is now waiting for you to press Ctrl+C"); + // 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 core.run(stream.for_each(|()| { println!("Ctrl-C received!"); Ok(()) From efef5a6e6a3a301bc286c4f1ae731c91ddf077d2 Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:20:51 +0200 Subject: [PATCH 04/10] Ctrl+C example: clarify control flow after receiving Ctrl+C: unreachable!() --- examples/ctrl-c/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/ctrl-c/src/main.rs b/examples/ctrl-c/src/main.rs index cee695806..be8962215 100644 --- a/examples/ctrl-c/src/main.rs +++ b/examples/ctrl-c/src/main.rs @@ -23,4 +23,7 @@ fn main() { println!("Ctrl-C received!"); Ok(()) })).unwrap(); + + println!("this won't be printed, because the received Ctrl+C will also kill the program"); + unreachable!(); } From 95d22930dd89e3866a894fff2474c497640a136a Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:23:10 +0200 Subject: [PATCH 05/10] Ctrl+C example: highlight power of Stream::for_each() --- examples/ctrl-c/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/ctrl-c/src/main.rs b/examples/ctrl-c/src/main.rs index be8962215..16edd592a 100644 --- a/examples/ctrl-c/src/main.rs +++ b/examples/ctrl-c/src/main.rs @@ -16,13 +16,18 @@ fn main() { println!("This program is now waiting for you to press Ctrl+C"); + // 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. + let future = stream.for_each(|()| { + println!("Ctrl+C received!"); + Ok(()) + }); + // 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 - core.run(stream.for_each(|()| { - println!("Ctrl-C received!"); - Ok(()) - })).unwrap(); + core.run(future).unwrap(); println!("this won't be printed, because the received Ctrl+C will also kill the program"); unreachable!(); From 51a7b6536a68aba37a299993b3361c961d2bc05e Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:30:25 +0200 Subject: [PATCH 06/10] Ctrl+C example: Don't forget proper attribution for original example --- examples/ctrl-c/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ctrl-c/Cargo.toml b/examples/ctrl-c/Cargo.toml index 80716c381..757504b5e 100644 --- a/examples/ctrl-c/Cargo.toml +++ b/examples/ctrl-c/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ctrl-c" version = "0.1.0" -authors = ["Jules Kerssemakers "] +authors = ["Jules Kerssemakers ", "Alex Crichton "] [dependencies] futures = "0.1.14" From 639cb29431468b365470f7f13c8f630c6b7078e6 Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Wed, 7 Jun 2017 21:47:08 +0200 Subject: [PATCH 07/10] undo nested example cargo project .. after learning about `cargo run --example` --- examples/{ctrl-c/src/main.rs => ctrl-c.rs} | 0 examples/ctrl-c/Cargo.toml | 9 --------- 2 files changed, 9 deletions(-) rename examples/{ctrl-c/src/main.rs => ctrl-c.rs} (100%) delete mode 100644 examples/ctrl-c/Cargo.toml diff --git a/examples/ctrl-c/src/main.rs b/examples/ctrl-c.rs similarity index 100% rename from examples/ctrl-c/src/main.rs rename to examples/ctrl-c.rs diff --git a/examples/ctrl-c/Cargo.toml b/examples/ctrl-c/Cargo.toml deleted file mode 100644 index 757504b5e..000000000 --- a/examples/ctrl-c/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ctrl-c" -version = "0.1.0" -authors = ["Jules Kerssemakers ", "Alex Crichton "] - -[dependencies] -futures = "0.1.14" -tokio-core = "0.1.7" -tokio-signal = "0.1" From c4e91718e6eafb42d33979077d1b8d3a1efc0a6a Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Thu, 8 Jun 2017 15:57:06 +0200 Subject: [PATCH 08/10] Ctrl+C example: Defer stream initialisation (and explain how/why) --- examples/ctrl-c.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/ctrl-c.rs b/examples/ctrl-c.rs index 16edd592a..6e3a10a55 100644 --- a/examples/ctrl-c.rs +++ b/examples/ctrl-c.rs @@ -2,7 +2,7 @@ extern crate futures; extern crate tokio_core; extern crate tokio_signal; -use futures::stream::Stream; +use futures::{Stream, Future}; use tokio_core::reactor::Core; fn main() { @@ -10,9 +10,13 @@ fn main() { let mut core = Core::new().unwrap(); // tokio_signal provides a convenience builder for Ctrl+C - // this even works cross-platform, linux and windows! - let ctrlc = tokio_signal::ctrl_c(&core.handle()); - let stream = core.run(ctrlc).unwrap(); + // 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 stream = tokio_signal::ctrl_c(&core.handle()).flatten_stream(); println!("This program is now waiting for you to press Ctrl+C"); From 01d8b7c28c58df7a10dfda8947f116544d824c5f Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Thu, 8 Jun 2017 15:59:04 +0200 Subject: [PATCH 09/10] Ctrl+C example: more explanations --- examples/ctrl-c.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/ctrl-c.rs b/examples/ctrl-c.rs index 6e3a10a55..fe9db7771 100644 --- a/examples/ctrl-c.rs +++ b/examples/ctrl-c.rs @@ -18,13 +18,19 @@ fn main() { // stream we want, reducing boilerplate Future-handling. let stream = tokio_signal::ctrl_c(&core.handle()).flatten_stream(); - println!("This program is now waiting for you to press Ctrl+C"); + println!("This program is now waiting for you to press Ctrl+C + * If running via `cargo run --example ctrl-c`, Ctrl+C also kills it, \ + due to https://github.com/rust-lang-nursery/rustup.rs/issues/806 + * If running the binary directly, the Ctrl+C is properly trapped. \ + Terminate by opening a second terminal and issue `pkill -sigkil ctrl-c`"); - // 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. + // 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 = stream.for_each(|()| { println!("Ctrl+C received!"); + + // return Ok-result to continue handling the stream Ok(()) }); From 79fb01afc730e350d46d8ca258d655d6a1c7cbd2 Mon Sep 17 00:00:00 2001 From: Jules Kerssemakers Date: Thu, 8 Jun 2017 21:32:50 +0200 Subject: [PATCH 10/10] Ctrl+C example: quit after 10 signals. --- examples/ctrl-c.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/ctrl-c.rs b/examples/ctrl-c.rs index fe9db7771..ae67cac17 100644 --- a/examples/ctrl-c.rs +++ b/examples/ctrl-c.rs @@ -5,6 +5,9 @@ extern crate tokio_signal; use futures::{Stream, Future}; use tokio_core::reactor::Core; +/// how many signals to handle before exiting +const STOP_AFTER: u64 = 10; + fn main() { // set up a Tokio event loop let mut core = Core::new().unwrap(); @@ -16,19 +19,32 @@ fn main() { // 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 stream = tokio_signal::ctrl_c(&core.handle()).flatten_stream(); + let endless_stream = tokio_signal::ctrl_c(&core.handle()).flatten_stream(); + // don't keep going forever: convert the endless stream to a bounded one. + let limited_stream = endless_stream.take(STOP_AFTER); - println!("This program is now waiting for you to press Ctrl+C + // how many Ctrl+C have we received so far? + let mut counter = 0; + + println!("This program is now waiting for you to press Ctrl+C {0} times. * If running via `cargo run --example ctrl-c`, Ctrl+C also kills it, \ due to https://github.com/rust-lang-nursery/rustup.rs/issues/806 - * If running the binary directly, the Ctrl+C is properly trapped. \ - Terminate by opening a second terminal and issue `pkill -sigkil ctrl-c`"); + * If running the binary directly, the Ctrl+C is properly trapped. + Terminate by repeating Ctrl+C {0} times, or ahead of time by \ + opening a second terminal and issuing `pkill -sigkil ctrl-c`", + STOP_AFTER); // 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 = stream.for_each(|()| { - println!("Ctrl+C received!"); + 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); // return Ok-result to continue handling the stream Ok(()) @@ -39,6 +55,5 @@ fn main() { // on our event loop core.run(future).unwrap(); - println!("this won't be printed, because the received Ctrl+C will also kill the program"); - unreachable!(); + println!("Stream ended, quiting the program."); }