Skip to content

Commit 6105a9d

Browse files
committed
Future::boxed and Stream::boxed should prevent double boxing
Fixes #511
1 parent 9f03c50 commit 6105a9d

File tree

3 files changed

+100
-2
lines changed

3 files changed

+100
-2
lines changed

src/future/mod.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,26 @@ 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+
use std::any::TypeId;
312+
use std::mem;
313+
use std::ptr;
314+
use std::boxed;
315+
316+
// Prevent double boxing
317+
if TypeId::of::<Self>() == TypeId::of::<BoxFuture<Self::Item, Self::Error>>() {
318+
assert!(mem::size_of::<Self>() == mem::size_of::<BoxFuture<Self::Item, Self::Error>>());
319+
unsafe {
320+
let mut r: BoxFuture<Self::Item, Self::Error> = mem::uninitialized();
321+
ptr::copy_nonoverlapping(
322+
&self as *const Self,
323+
&mut r as *mut BoxFuture<Self::Item, Self::Error> as *mut u8 as *mut Self,
324+
1);
325+
mem::forget(self);
326+
r
327+
}
328+
} else {
329+
boxed::Box::new(self)
330+
}
312331
}
313332

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

src/stream/mod.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,26 @@ 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+
use std::any::TypeId;
254+
use std::mem;
255+
use std::ptr;
256+
use std::boxed;
257+
258+
// Prevent double boxing
259+
if TypeId::of::<Self>() == TypeId::of::<BoxStream<Self::Item, Self::Error>>() {
260+
assert!(mem::size_of::<Self>() == mem::size_of::<BoxStream<Self::Item, Self::Error>>());
261+
unsafe {
262+
let mut r: BoxStream<Self::Item, Self::Error> = mem::uninitialized();
263+
ptr::copy_nonoverlapping(
264+
&self as *const Self,
265+
&mut r as *mut BoxStream<Self::Item, Self::Error> as *mut u8 as *mut Self,
266+
1);
267+
mem::forget(self);
268+
r
269+
}
270+
} else {
271+
boxed::Box::new(self)
272+
}
254273
}
255274

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

tests/boxed.rs

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

0 commit comments

Comments
 (0)