From 003925f0c6ece2536b13622aa329938cbba37856 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Tue, 13 Jun 2017 11:20:04 +0300 Subject: [PATCH 1/2] Bench boxed boxed --- benches/boxed_boxed.rs | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 benches/boxed_boxed.rs diff --git a/benches/boxed_boxed.rs b/benches/boxed_boxed.rs new file mode 100644 index 0000000000..a7bf0bb977 --- /dev/null +++ b/benches/boxed_boxed.rs @@ -0,0 +1,49 @@ +#![feature(test)] + +extern crate test; +extern crate futures; + +use test::Bencher; + +use futures::Async; +use futures::stream; +use futures::stream::Stream; + + +fn drain>(mut s: S) { + loop { + match s.poll() { + Ok(Async::Ready(Some(i))) => { test::black_box(i); }, + Ok(Async::Ready(None)) => return, + _ => unreachable!(), + } + } +} + +#[bench] +fn plain(b: &mut Bencher) { + b.iter(|| { + drain(stream::iter((0..1000).map(Ok))); + }) +} + +#[bench] +fn boxed(b: &mut Bencher) { + b.iter(|| { + drain(stream::iter((0..1000).map(Ok)).boxed()); + }) +} + +#[bench] +fn boxed_boxed(b: &mut Bencher) { + b.iter(|| { + drain(stream::iter((0..1000).map(Ok)).boxed().boxed()); + }) +} + +#[bench] +fn boxed_boxed_boxed(b: &mut Bencher) { + b.iter(|| { + drain(stream::iter((0..1000).map(Ok)).boxed().boxed().boxed()); + }) +} From 757588635f30b05d00a35626b0ea8fcbba3198de Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sun, 11 Jun 2017 02:32:19 +0300 Subject: [PATCH 2/2] Future::boxed and Stream::boxed should prevent double boxing Fixes #511 --- Cargo.toml | 3 +++ build.rs | 56 ++++++++++++++++++++++++++++++++++++++++++ src/future/mod.rs | 29 +++++++++++++++++++++- src/lib.rs | 2 ++ src/stream/mod.rs | 29 +++++++++++++++++++++- tests/boxed.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 build.rs create mode 100644 tests/boxed.rs diff --git a/Cargo.toml b/Cargo.toml index ae4bed2cda..69220ee88b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ appveyor = { repository = "alexcrichton/futures-rs" } [dependencies] +[build-dependencies] +regex = "0.2" + [features] use_std = [] with-deprecated = [] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..90065970f7 --- /dev/null +++ b/build.rs @@ -0,0 +1,56 @@ +extern crate regex; + +use std::env; +use std::str; +use std::process; + +#[allow(dead_code)] +struct RustVersion { + major: u32, + minor: u32, + patch: u32, + nightly: bool, +} + +fn rust_version() -> RustVersion { + let rustc = env::var("RUSTC").expect("RUSTC variable is unset"); + + let command = process::Command::new(rustc) + .args(&["--version"]) + .stdin(process::Stdio::null()) + .stderr(process::Stdio::inherit()) + .stdout(process::Stdio::piped()) + .spawn() + .expect("spawn rustc"); + + let wait = command.wait_with_output().expect("wait for rust"); + if !wait.status.success() { + panic!("rustc --version exited with non-zero code"); + } + + let stdout = str::from_utf8(&wait.stdout).expect("stdout is not UTF-8"); + + let re = regex::Regex::new(r"^rustc (\d+)\.(\d+)\.(\d+)(-nightly)?").expect("compile regex"); + let captures = re.captures(stdout) + .expect(&format!("regex cannot match `rustc --version` output: {:?}", stdout)); + + let major: u32 = captures.get(1).expect("major").as_str().parse().unwrap(); + let minor: u32 = captures.get(2).expect("minor").as_str().parse().unwrap(); + let patch: u32 = captures.get(3).expect("patch").as_str().parse().unwrap(); + let nightly: bool = captures.get(4).is_some(); + + RustVersion { + major: major, + minor: minor, + patch: patch, + nightly: nightly, + } +} + +fn main() { + let version = rust_version(); + + if version.nightly { + println!("cargo:rustc-cfg=rust_nightly"); + } +} diff --git a/src/future/mod.rs b/src/future/mod.rs index 7e278bac88..fe44a4bdda 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -308,7 +308,7 @@ pub trait Future { fn boxed(self) -> BoxFuture where Self: Sized + Send + 'static { - ::std::boxed::Box::new(self) + Boxed::boxed(self) } /// Map this future's result to a different type, returning a new future of @@ -1044,3 +1044,30 @@ impl fmt::Debug for ExecuteError { } } } + + +#[cfg(feature = "use_std")] +trait Boxed: Future { + fn boxed(self) -> BoxFuture; +} + +#[cfg(feature = "use_std")] +impl Boxed for F where F: Future + Sized + Send + 'static { + #[cfg(rust_nightly)] + default fn boxed(self) -> BoxFuture { + ::std::boxed::Box::new(self) + } + + #[cfg(not(rust_nightly))] + fn boxed(self) -> BoxFuture { + ::std::boxed::Box::new(self) + } +} + +#[cfg(feature = "use_std")] +#[cfg(rust_nightly)] +impl Boxed for BoxFuture { + fn boxed(self) -> BoxFuture { + self + } +} diff --git a/src/lib.rs b/src/lib.rs index ed8c93f96d..c6bba2e2f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(rust_nightly, feature(specialization))] + //! Zero-cost Futures in Rust //! //! This library is an implementation of futures in Rust which aims to provide diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8a7e466ca8..c7c9e6cd23 100755 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -250,7 +250,7 @@ pub trait Stream { fn boxed(self) -> BoxStream where Self: Sized + Send + 'static, { - ::std::boxed::Box::new(self) + Boxed::boxed(self) } /// Converts this stream into a `Future`. @@ -1059,3 +1059,30 @@ impl<'a, S: ?Sized + Stream> Stream for &'a mut S { (**self).poll() } } + + +#[cfg(feature = "use_std")] +trait Boxed: Stream { + fn boxed(self) -> BoxStream; +} + +#[cfg(feature = "use_std")] +impl Boxed for F where F: Stream + Sized + Send + 'static { + #[cfg(rust_nightly)] + default fn boxed(self) -> BoxStream { + ::std::boxed::Box::new(self) + } + + #[cfg(not(rust_nightly))] + fn boxed(self) -> BoxStream { + ::std::boxed::Box::new(self) + } +} + +#[cfg(feature = "use_std")] +#[cfg(rust_nightly)] +impl Boxed for BoxStream { + fn boxed(self) -> BoxStream { + self + } +} diff --git a/tests/boxed.rs b/tests/boxed.rs new file mode 100644 index 0000000000..8bc87d3e45 --- /dev/null +++ b/tests/boxed.rs @@ -0,0 +1,62 @@ +#![cfg(rust_nightly)] + +extern crate futures; + +use futures::Async; +use futures::Poll; +use futures::future::Future; +use futures::stream::Stream; + + +#[test] +fn future_boxed_prevents_double_boxing() { + struct MyFuture { + r: &'static str, + } + + impl Future for MyFuture { + type Item = &'static str; + type Error = (); + + fn poll(&mut self) -> Poll { + Ok(Async::Ready(self.r)) + } + } + + let f = MyFuture { r: "I'm ready" }; + let f = f.boxed(); + let ptr = f.as_ref() as *const Future; + let f = f.boxed(); + let f = f.boxed(); + let mut f = f.boxed(); + assert_eq!(f.as_ref() as *const Future, ptr); + assert_eq!(Ok(Async::Ready("I'm ready")), f.poll()); +} + +#[test] +fn stream_boxed_prevents_double_boxing() { + struct MyStream { + i: u32, + } + + impl Stream for MyStream { + type Item = u32; + type Error = (); + + fn poll(&mut self) -> Poll, Self::Error> { + self.i += 1; + Ok(Async::Ready(Some(self.i))) + } + } + + let s = MyStream { i: 0 }; + let s = s.boxed(); + let ptr = s.as_ref() as *const Stream; + let s = s.boxed(); + let s = s.boxed(); + let mut s = s.boxed(); + assert_eq!(s.as_ref() as *const Stream, ptr); + assert_eq!(Ok(Async::Ready(Some(1))), s.poll()); + assert_eq!(Ok(Async::Ready(Some(2))), s.poll()); + assert_eq!(Ok(Async::Ready(Some(3))), s.poll()); +}