Skip to content

Commit

Permalink
rollup merge of rust-lang#23939: nikomatsakis/fn-box
Browse files Browse the repository at this point in the history
Conflicts:
	src/liballoc/boxed.rs
  • Loading branch information
alexcrichton committed Apr 1, 2015
2 parents e9bacba + 8eed73f commit 9bb05fd
Show file tree
Hide file tree
Showing 17 changed files with 124 additions and 85 deletions.
3 changes: 1 addition & 2 deletions src/compiletest/compiletest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ extern crate log;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::thunk::Thunk;
use getopts::{optopt, optflag, reqopt};
use common::Config;
use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
Expand Down Expand Up @@ -351,7 +350,7 @@ pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
let config = (*config).clone();
let testfile = testfile.to_path_buf();
test::DynTestFn(Thunk::new(move || {
test::DynTestFn(Box::new(move || {
runtest::run(config, &testfile)
}))
}
Expand Down
71 changes: 71 additions & 0 deletions src/liballoc/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,74 @@ impl<I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for Box<I> {
#[stable(feature = "rust1", since = "1.0.0")]
impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Box<I> {}


/// `FnBox` is a version of the `FnOnce` intended for use with boxed
/// closure objects. The idea is that where one would normally store a
/// `Box<FnOnce()>` in a data structure, you should use
/// `Box<FnBox()>`. The two traits behave essentially the same, except
/// that a `FnBox` closure can only be called if it is boxed. (Note
/// that `FnBox` may be deprecated in the future if `Box<FnOnce()>`
/// closures become directly usable.)
///
/// ### Example
///
/// Here is a snippet of code which creates a hashmap full of boxed
/// once closures and then removes them one by one, calling each
/// closure as it is removed. Note that the type of the closures
/// stored in the map is `Box<FnBox() -> i32>` and not `Box<FnOnce()
/// -> i32>`.
///
/// ```
/// #![feature(core)]
///
/// use std::boxed::FnBox;
/// use std::collections::HashMap;
///
/// fn make_map() -> HashMap<i32, Box<FnBox() -> i32>> {
/// let mut map: HashMap<i32, Box<FnBox() -> i32>> = HashMap::new();
/// map.insert(1, Box::new(|| 22));
/// map.insert(2, Box::new(|| 44));
/// map
/// }
///
/// fn main() {
/// let mut map = make_map();
/// for i in &[1, 2] {
/// let f = map.remove(&i).unwrap();
/// assert_eq!(f(), i * 22);
/// }
/// }
/// ```
#[rustc_paren_sugar]
#[unstable(feature = "core", reason = "Newly introduced")]
pub trait FnBox<A> {
type Output;

fn call_box(self: Box<Self>, args: A) -> Self::Output;
}

impl<A,F> FnBox<A> for F
where F: FnOnce<A>
{
type Output = F::Output;

fn call_box(self: Box<F>, args: A) -> F::Output {
self.call_once(args)
}
}

impl<'a,A,R> FnOnce<A> for Box<FnBox<A,Output=R>+'a> {
type Output = R;

extern "rust-call" fn call_once(self, args: A) -> R {
self.call_box(args)
}
}

impl<'a,A,R> FnOnce<A> for Box<FnBox<A,Output=R>+Send+'a> {
type Output = R;

extern "rust-call" fn call_once(self, args: A) -> R {
self.call_box(args)
}
}
3 changes: 1 addition & 2 deletions src/librustdoc/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use std::path::PathBuf;
use std::process::Command;
use std::str;
use std::sync::{Arc, Mutex};
use std::thunk::Thunk;

use testing;
use rustc_lint;
Expand Down Expand Up @@ -366,7 +365,7 @@ impl Collector {
ignore: should_ignore,
should_panic: testing::ShouldPanic::No, // compiler failures are test failures
},
testfn: testing::DynTestFn(Thunk::new(move|| {
testfn: testing::DynTestFn(Box::new(move|| {
runtest(&test,
&cratename,
libs,
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ mod uint_macros;
#[path = "num/f64.rs"] pub mod f64;

pub mod ascii;

pub mod thunk;

/* Common traits */
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/rt/at_exit_imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub fn cleanup() {
if queue as usize != 0 {
let queue: Box<Queue> = Box::from_raw(queue);
for to_run in *queue {
to_run.invoke(());
to_run();
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/libstd/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

use prelude::v1::*;
use sys;
use thunk::Thunk;
use usize;

// Reexport some of our utilities which are expected by other crates.
Expand Down Expand Up @@ -153,7 +152,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
/// that the closure could not be registered, meaning that it is not scheduled
/// to be rune.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Thunk::new(f)) {Ok(())} else {Err(())}
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}

/// One-time runtime cleanup.
Expand Down
5 changes: 3 additions & 2 deletions src/libstd/sync/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use core::prelude::*;
use core::mem::replace;

use boxed::Box;
use self::FutureState::*;
use sync::mpsc::{Receiver, channel};
use thunk::Thunk;
Expand Down Expand Up @@ -84,7 +85,7 @@ impl<A> Future<A> {
match replace(&mut self.state, Evaluating) {
Forced(_) | Evaluating => panic!("Logic error."),
Pending(f) => {
self.state = Forced(f.invoke(()));
self.state = Forced(f());
self.get_ref()
}
}
Expand Down Expand Up @@ -114,7 +115,7 @@ impl<A> Future<A> {
* function. It is not spawned into another task.
*/

Future {state: Pending(Thunk::new(f))}
Future {state: Pending(Box::new(f))}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/libstd/sys/common/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn start_thread(main: *mut libc::c_void) {
unsafe {
stack::record_os_managed_stack_bounds(0, usize::MAX);
let _handler = stack_overflow::Handler::new();
Box::from_raw(main as *mut Thunk).invoke(());
let main: Box<Thunk> = Box::from_raw(main as *mut Thunk);
main();
}
}
28 changes: 16 additions & 12 deletions src/libstd/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ impl Builder {
pub fn spawn<F>(self, f: F) -> io::Result<JoinHandle> where
F: FnOnce(), F: Send + 'static
{
self.spawn_inner(Thunk::new(f)).map(|i| JoinHandle(i))
self.spawn_inner(Box::new(f)).map(|i| JoinHandle(i))
}

/// Spawn a new child thread that must be joined within a given
Expand All @@ -279,7 +279,7 @@ impl Builder {
pub fn scoped<'a, T, F>(self, f: F) -> io::Result<JoinGuard<'a, T>> where
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a
{
self.spawn_inner(Thunk::new(f)).map(|inner| {
self.spawn_inner(Box::new(f)).map(|inner| {
JoinGuard { inner: inner, _marker: PhantomData }
})
}
Expand Down Expand Up @@ -315,7 +315,7 @@ impl Builder {
thread_info::set(imp::guard::current(), their_thread);
}

let mut output = None;
let mut output: Option<T> = None;
let try_result = {
let ptr = &mut output;

Expand All @@ -327,7 +327,11 @@ impl Builder {
// 'unwinding' flag in the thread itself. For these reasons,
// this unsafety should be ok.
unsafe {
unwind::try(move || *ptr = Some(f.invoke(())))
unwind::try(move || {
let f: Thunk<(), T> = f;
let v: T = f();
*ptr = Some(v)
})
}
};
unsafe {
Expand All @@ -340,7 +344,7 @@ impl Builder {
};

Ok(JoinInner {
native: try!(unsafe { imp::create(stack_size, Thunk::new(main)) }),
native: try!(unsafe { imp::create(stack_size, Box::new(main)) }),
thread: my_thread,
packet: my_packet,
joined: false,
Expand Down Expand Up @@ -820,7 +824,7 @@ mod test {
let x: Box<_> = box 1;
let x_in_parent = (&*x) as *const i32 as usize;

spawnfn(Thunk::new(move|| {
spawnfn(Box::new(move|| {
let x_in_child = (&*x) as *const i32 as usize;
tx.send(x_in_child).unwrap();
}));
Expand All @@ -832,15 +836,15 @@ mod test {
#[test]
fn test_avoid_copying_the_body_spawn() {
avoid_copying_the_body(|v| {
thread::spawn(move || v.invoke(()));
thread::spawn(move || v());
});
}

#[test]
fn test_avoid_copying_the_body_thread_spawn() {
avoid_copying_the_body(|f| {
thread::spawn(move|| {
f.invoke(());
f();
});
})
}
Expand All @@ -849,7 +853,7 @@ mod test {
fn test_avoid_copying_the_body_join() {
avoid_copying_the_body(|f| {
let _ = thread::spawn(move|| {
f.invoke(())
f()
}).join();
})
}
Expand All @@ -862,13 +866,13 @@ mod test {
// valgrind-friendly. try this at home, instead..!)
const GENERATIONS: u32 = 16;
fn child_no(x: u32) -> Thunk<'static> {
return Thunk::new(move|| {
return Box::new(move|| {
if x < GENERATIONS {
thread::spawn(move|| child_no(x+1).invoke(()));
thread::spawn(move|| child_no(x+1)());
}
});
}
thread::spawn(|| child_no(0).invoke(()));
thread::spawn(|| child_no(0)());
}

#[test]
Expand Down
42 changes: 3 additions & 39 deletions src/libstd/thunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,9 @@
#![allow(missing_docs)]
#![unstable(feature = "std_misc")]

use alloc::boxed::Box;
use alloc::boxed::{Box, FnBox};
use core::marker::Send;
use core::ops::FnOnce;

pub struct Thunk<'a, A=(),R=()> {
invoke: Box<Invoke<A,R>+Send + 'a>,
}
pub type Thunk<'a, A=(), R=()> =
Box<FnBox<A,Output=R> + Send + 'a>;

impl<'a, R> Thunk<'a,(),R> {
pub fn new<F>(func: F) -> Thunk<'a,(),R>
where F : FnOnce() -> R, F : Send + 'a
{
Thunk::with_arg(move|()| func())
}
}

impl<'a,A,R> Thunk<'a,A,R> {
pub fn with_arg<F>(func: F) -> Thunk<'a,A,R>
where F : FnOnce(A) -> R, F : Send + 'a
{
Thunk {
invoke: Box::<F>::new(func)
}
}

pub fn invoke(self, arg: A) -> R {
self.invoke.invoke(arg)
}
}

pub trait Invoke<A=(),R=()> {
fn invoke(self: Box<Self>, arg: A) -> R;
}

impl<A,R,F> Invoke<A,R> for F
where F : FnOnce(A) -> R
{
fn invoke(self: Box<F>, arg: A) -> R {
let f = *self;
f(arg)
}
}
Loading

0 comments on commit 9bb05fd

Please sign in to comment.