Skip to content

Commit 898e6db

Browse files
committed
Make MAX_UNPARK_BYTES configurable and expose it. Make UnparkHandle not Sync.
MAX_UNPARK_BYTES is now exposed and configurable at compile-time by setting the FUTURES_MAX_UNPARK_BYTES env var. UnparkHandle is marked non-Sync therefore Task is non-Sync.
1 parent f5ec679 commit 898e6db

File tree

5 files changed

+44
-28
lines changed

5 files changed

+44
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ An implementation of futures and streams featuring zero allocations,
1313
composability, and iterator-like interfaces.
1414
"""
1515
categories = ["asynchronous"]
16+
build = "build.rs"
1617

1718
[badges]
1819
travis-ci = { repository = "alexcrichton/futures-rs" }

build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use std::env;
2+
use std::fs::File;
3+
use std::io::Write;
4+
use std::path::Path;
5+
use std::str::FromStr;
6+
7+
const MAX_SIZE_ENV : Option<&'static str> = option_env!("FUTURES_MAX_UNPARK_BYTES");
8+
9+
fn main() {
10+
let out_dir = env::var("OUT_DIR").unwrap();
11+
let max_size = match MAX_SIZE_ENV.and_then(|s| usize::from_str(&s).ok()) {
12+
Some(x) if x > 0 => x,
13+
_ => 64 // Default value.
14+
};
15+
let dest_path = Path::new(&out_dir).join("max_unpark_bytes.txt");
16+
let mut f = File::create(&dest_path).unwrap();
17+
f.write_all(max_size.to_string().as_bytes()).unwrap();
18+
}

src/executor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
//!
88
//! [online]: https://tokio.rs/docs/going-deeper/tasks/
99
10-
pub use task_impl::{Spawn, spawn, Unpark, Executor, Run, UnparkHandle};
10+
pub use task_impl::{Spawn, spawn, Unpark, Executor, Run, UnparkHandle, MAX_UNPARK_BYTES};

src/task_impl/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod data;
2020
pub use self::task_rc::TaskRc;
2121
pub use self::data::LocalKey;
2222
pub use self::unpark_handle::UnparkHandle;
23+
pub use self::unpark_handle::MAX_UNPARK_BYTES;
2324

2425
struct BorrowedTask<'a> {
2526
id: usize,

src/task_impl/unpark_handle.rs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
use core::ptr;
22
use core::mem::{forget, size_of};
3+
use core::marker::PhantomData;
4+
use core::cell::Cell;
35
use super::Unpark;
46

5-
/// Maximum size in bytes that will fit in a `UnparkObject`.
6-
/// TODO: What should this value be?
7-
/// Should we expose this?
8-
/// We probably want to say that this value may increase but never decrease in a 1.x release.
9-
const MAX_OBJ_BYTES: usize = 64;
7+
/// Maximum size of `T` in `UnparkHandle<T>`, in bytes.
8+
/// Configurable by the FUTURES_MAX_UNPARK_BYTES env var at compile-time.
9+
pub const MAX_UNPARK_BYTES: usize = include!(concat!(env!("OUT_DIR"), "/max_unpark_bytes.txt"));
1010

1111
/// A VTable that knows how to clone because the data has a maximum size.
1212
#[derive(Copy)]
1313
struct UnparkVtable {
1414
unpark: fn(*const ()),
15-
clone_to_byte_buffer: fn(*const ()) -> [u8; MAX_OBJ_BYTES],
15+
clone_to_byte_buffer: fn(*const ()) -> [u8; MAX_UNPARK_BYTES],
1616
drop_in_place: unsafe fn(*mut ()),
1717
}
1818

@@ -24,7 +24,7 @@ impl Clone for UnparkVtable {
2424

2525
impl UnparkVtable {
2626
fn new<T: Unpark + Clone>() -> UnparkVtable {
27-
assert!(size_of::<T>() <= MAX_OBJ_BYTES);
27+
assert!(size_of::<T>() <= MAX_UNPARK_BYTES);
2828
UnparkVtable {
2929
unpark: Self::call_unpark::<T>,
3030
clone_to_byte_buffer: Self::clone_to_byte_buffer::<T>,
@@ -37,9 +37,9 @@ impl UnparkVtable {
3737
downcasted.unpark()
3838
}
3939

40-
/// Returns array with bytes of the cloned data. Make sure data is shorter than MAX_OBJ_BYTES.
40+
/// Returns array with bytes of the cloned data. Safe if data is shorter than MAX_UNPARK_BYTES.
4141
/// The caller owns the new data and is responsible for dropping it with `drop_in_place<T>`.
42-
fn clone_to_byte_buffer<T: Clone>(data: *const ()) -> [u8; MAX_OBJ_BYTES] {
42+
fn clone_to_byte_buffer<T: Clone>(data: *const ()) -> [u8; MAX_UNPARK_BYTES] {
4343
let downcasted = unsafe { &*(data as *const _ as *const T) };
4444
obliviate(downcasted.clone())
4545
}
@@ -57,7 +57,6 @@ impl UnparkVtable {
5757
/// an `Arc` before passing it to the `UnparkHandle`.
5858
///
5959
/// # Deciding whether to use an `Arc`
60-
/// If your `unpark` is not `Clone` or has lifetime parameters then you must use an `Arc`.
6160
/// If you use inner mutability in your `unpark`, then you should carefully
6261
/// consider what happens when it is cloned and what is the behaviour you want.
6362
/// Inner mutability aside, the only difference between using or not an `Arc` should be performance.
@@ -68,35 +67,32 @@ impl UnparkVtable {
6867
pub struct UnparkHandle {
6968
// A custom trait object that takes ownership of the data as a slice of bytes.
7069
// Can clone it's `data`, goes inside a `Task`.
71-
data: [u8; MAX_OBJ_BYTES],
70+
data: [u8; MAX_UNPARK_BYTES],
7271
vtable: UnparkVtable,
72+
not_sync : PhantomData<Cell<()>> // Cell is Send but not Sync, convenient.
7373
}
7474

75-
// Means `Task` is not `Sync`.
76-
//impl !Sync for UnparkHandle {}
77-
7875
impl UnparkHandle {
7976
/// Constructs a `UnparkHandle`.
8077
/// A `Task` handle returned by `park` will contain a clone of the `unpark`
8178
/// argument provided here. You may want to wrap `unpark` in an `Arc`.
8279
///
8380
/// # Panic
8481
/// Panics if the size of 'T' is too large. If you get a panic try wrapping the argument
85-
/// in an `Arc`.
82+
/// in an `Arc` or setting the FUTURES_MAX_UNPARK_BYTES env var to the size of `T`.
83+
/// After changing FUTURES_MAX_UNPARK_BYTES you need to recompile `futures`.
8684
pub fn new<T: Unpark + Clone + 'static>(unpark: T) -> UnparkHandle {
87-
if size_of::<T>() > MAX_OBJ_BYTES {
88-
// TODO: Panicking here seems reasonable and could be a compile time error when we
89-
// get a const sytem in Rust. But what about libraries that pass a user supplied type as T?
90-
// Should we expose MAX_OBJ_BYTES? Offer a version that return an error?
91-
// We could auto-fallback to Arc, with the downside that we need two UnparkHandle constructors,
92-
// one with fallback where the user doesn't care because Arc doesn't change his semantics,
93-
// one without fallback where he cares and wants to decide for himself between Arc or no Arc.
94-
panic!("The size of T is {} bytes which is too large. Try wrapping the unpark argument in an Arc.");
85+
if size_of::<T>() > MAX_UNPARK_BYTES {
86+
// Panicking is reasonable since it's easy to catch and fix when testing.
87+
// Could be a compile time error when we get a const system in Rust.
88+
// Libraries that pass a user supplied T should do this check themselves if they want to avoid the panic.
89+
panic!("The size of T is {} bytes which is too large. Try wrapping the unpark argument in an Arc or setting the FUTURES_MAX_UNPARK_BYTES env var.");
9590
}
9691

9792
UnparkHandle {
9893
data: obliviate(unpark),
9994
vtable: UnparkVtable::new::<T>(),
95+
not_sync: PhantomData
10096
}
10197
}
10298
}
@@ -113,7 +109,7 @@ impl Clone for UnparkHandle {
113109
fn clone(&self) -> Self {
114110
UnparkHandle {
115111
data: (self.vtable.clone_to_byte_buffer)(&self.data as *const _ as *const ()),
116-
vtable: self.vtable,
112+
..*self
117113
}
118114
}
119115
}
@@ -126,10 +122,10 @@ impl Unpark for UnparkHandle {
126122

127123
/// Turns the victim into raw bytes and forgets it.
128124
/// The caller now owns the value and is responsible for dropping it with 'drop_in_place<T>'.
129-
fn obliviate<T>(victim : T) -> [u8; MAX_OBJ_BYTES] {
125+
fn obliviate<T>(victim : T) -> [u8; MAX_UNPARK_BYTES] {
130126
let size = size_of::<T>();
131-
assert!(size < MAX_OBJ_BYTES);
132-
let mut buffer = [0; MAX_OBJ_BYTES];
127+
assert!(size < MAX_UNPARK_BYTES);
128+
let mut buffer = [0; MAX_UNPARK_BYTES];
133129
// View victim and buffer as raw bytes.
134130
let victim_ptr = &victim as *const _ as *const u8;
135131
let buffer_ptr = &mut buffer as *mut _ as *mut u8;

0 commit comments

Comments
 (0)