Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add (unstable) FnBox trait as a nicer replacement for Thunk. #23939

Merged
merged 3 commits into from
Apr 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -355,3 +355,74 @@ impl<'a, 'b> From<&'b str> for Box<Error + Send + 'a> {
}
}
}

/// `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=()> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a #[deprecated] tag to the module as well?

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