Skip to content

Commit be4984a

Browse files
committed
Future::boxed and Stream::boxed should prevent double boxing
Fixes rust-lang#511
1 parent 9f03c50 commit be4984a

File tree

5 files changed

+169
-2
lines changed

5 files changed

+169
-2
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ appveyor = { repository = "alexcrichton/futures-rs" }
2020

2121
[dependencies]
2222

23+
[build-dependencies]
24+
regex = "0.2"
25+
2326
[features]
2427
use_std = []
2528
with-deprecated = []

build.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
extern crate regex;
2+
3+
use std::env;
4+
use std::str;
5+
use std::process;
6+
7+
fn main() {
8+
let rustc = env::var("RUSTC").expect("RUSTC variable is unset");
9+
10+
let command = process::Command::new(rustc)
11+
.args(&["--version"])
12+
.stdin(process::Stdio::null())
13+
.stderr(process::Stdio::inherit())
14+
.stdout(process::Stdio::piped())
15+
.spawn()
16+
.expect("spawn rustc");
17+
18+
let wait = command.wait_with_output().expect("wait for rust");
19+
if !wait.status.success() {
20+
panic!("rustc --version exited with non-zero code");
21+
}
22+
23+
let stdout = str::from_utf8(&wait.stdout).expect("stdout is not UTF-8");
24+
25+
let re = regex::Regex::new(r"^rustc (\d+)\.(\d+)\.(\d+)").expect("compile regex");
26+
let captures = re.captures(stdout)
27+
.expect(&format!("regex cannot match `rustc --version` output: {:?}", stdout));
28+
29+
let major: u32 = captures.get(1).expect("major").as_str().parse().unwrap();
30+
let minor: u32 = captures.get(2).expect("minor").as_str().parse().unwrap();
31+
let _patch: u32 = captures.get(3).expect("patch").as_str().parse().unwrap();
32+
33+
if major > 1 || minor >= 18 {
34+
println!("cargo:rustc-cfg=rust_at_least_1_18");
35+
}
36+
}

src/future/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,40 @@ pub trait Future {
308308
fn boxed(self) -> BoxFuture<Self::Item, Self::Error>
309309
where Self: Sized + Send + 'static
310310
{
311-
::std::boxed::Box::new(self)
311+
#[cfg(rust_at_least_1_18)]
312+
fn boxed<F>(future: F) -> BoxFuture<F::Item, F::Error>
313+
where F: Future + Sized + Send + 'static
314+
{
315+
use std::any::TypeId;
316+
use std::mem;
317+
use std::ptr;
318+
use std::boxed;
319+
320+
// Prevent double boxing
321+
if TypeId::of::<F>() == TypeId::of::<BoxFuture<F::Item, F::Error>>() {
322+
assert!(mem::size_of::<F>() == mem::size_of::<BoxFuture<F::Item, F::Error>>());
323+
unsafe {
324+
let mut r: BoxFuture<F::Item, F::Error> = mem::uninitialized();
325+
ptr::copy_nonoverlapping(
326+
&future as *const F,
327+
&mut r as *mut BoxFuture<F::Item, F::Error> as *mut u8 as *mut F,
328+
1);
329+
mem::forget(future);
330+
r
331+
}
332+
} else {
333+
boxed::Box::new(future)
334+
}
335+
}
336+
337+
#[cfg(not(rust_at_least_1_18))]
338+
fn boxed<F>(future: F) -> BoxFuture<F::Item, F::Error>
339+
where F: Future + Sized + Send + 'static
340+
{
341+
::std::boxed::Box::new(future)
342+
}
343+
344+
boxed(self)
312345
}
313346

314347
/// Map this future's result to a different type, returning a new future of

src/stream/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,40 @@ pub trait Stream {
250250
fn boxed(self) -> BoxStream<Self::Item, Self::Error>
251251
where Self: Sized + Send + 'static,
252252
{
253-
::std::boxed::Box::new(self)
253+
#[cfg(rust_at_least_1_18)]
254+
fn boxed<S>(stream: S) -> BoxStream<S::Item, S::Error>
255+
where S: Stream + Sized + Send + 'static
256+
{
257+
use std::any::TypeId;
258+
use std::mem;
259+
use std::ptr;
260+
use std::boxed;
261+
262+
// Prevent double boxing
263+
if TypeId::of::<S>() == TypeId::of::<BoxStream<S::Item, S::Error>>() {
264+
assert!(mem::size_of::<S>() == mem::size_of::<BoxStream<S::Item, S::Error>>());
265+
unsafe {
266+
let mut r: BoxStream<S::Item, S::Error> = mem::uninitialized();
267+
ptr::copy_nonoverlapping(
268+
&stream as *const S,
269+
&mut r as *mut BoxStream<S::Item, S::Error> as *mut u8 as *mut S,
270+
1);
271+
mem::forget(stream);
272+
r
273+
}
274+
} else {
275+
boxed::Box::new(stream)
276+
}
277+
}
278+
279+
#[cfg(not(rust_at_least_1_18))]
280+
fn boxed<S>(stream: S) -> BoxStream<S::Item, S::Error>
281+
where S: Stream + Sized + Send + 'static
282+
{
283+
::std::boxed::Box::new(stream)
284+
}
285+
286+
boxed(self)
254287
}
255288

256289
/// Converts this stream into a `Future`.

tests/boxed.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![cfg(rust_at_least_1_18)]
2+
3+
extern crate futures;
4+
5+
use futures::Async;
6+
use futures::Poll;
7+
use futures::future::Future;
8+
use futures::stream::Stream;
9+
10+
11+
#[test]
12+
fn future_boxed_prevents_double_boxing() {
13+
struct MyFuture {
14+
r: &'static str,
15+
}
16+
17+
impl Future for MyFuture {
18+
type Item = &'static str;
19+
type Error = ();
20+
21+
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
22+
Ok(Async::Ready(self.r))
23+
}
24+
}
25+
26+
let f = MyFuture { r: "I'm ready" };
27+
let f = f.boxed();
28+
let ptr = f.as_ref() as *const Future<Item=_, Error=_>;
29+
let f = f.boxed();
30+
let f = f.boxed();
31+
let mut f = f.boxed();
32+
assert_eq!(f.as_ref() as *const Future<Item=_, Error=_>, ptr);
33+
assert_eq!(Ok(Async::Ready("I'm ready")), f.poll());
34+
}
35+
36+
#[test]
37+
fn stream_boxed_prevents_double_boxing() {
38+
struct MyStream {
39+
i: u32,
40+
}
41+
42+
impl Stream for MyStream {
43+
type Item = u32;
44+
type Error = ();
45+
46+
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
47+
self.i += 1;
48+
Ok(Async::Ready(Some(self.i)))
49+
}
50+
}
51+
52+
let s = MyStream { i: 0 };
53+
let s = s.boxed();
54+
let ptr = s.as_ref() as *const Stream<Item=_, Error=_>;
55+
let s = s.boxed();
56+
let s = s.boxed();
57+
let mut s = s.boxed();
58+
assert_eq!(s.as_ref() as *const Stream<Item=_, Error=_>, ptr);
59+
assert_eq!(Ok(Async::Ready(Some(1))), s.poll());
60+
assert_eq!(Ok(Async::Ready(Some(2))), s.poll());
61+
assert_eq!(Ok(Async::Ready(Some(3))), s.poll());
62+
}

0 commit comments

Comments
 (0)