Remove `Thunk` struct and `Invoke` trait; change `Thunk` to be an alias

for `Box<FnBox()>`. I found the alias was still handy because it is
shorter than the fully written type.

This is a [breaking-change]: convert code using `Invoke` to use `FnBox`,
which is usually pretty straight-forward. Code using thunk mostly works
if you change `Thunk::new => Box::new` and `foo.invoke(arg)` to
`foo(arg)`.
This commit is contained in:
Niko Matsakis 2015-04-01 11:12:30 -04:00
parent ed63d32651
commit cade32acf6
16 changed files with 56 additions and 93 deletions

View File

@ -31,7 +31,6 @@ extern crate log;
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::thunk::Thunk;
use getopts::{optopt, optflag, reqopt}; use getopts::{optopt, optflag, reqopt};
use common::Config; use common::Config;
use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen}; use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
@ -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 { pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
let config = (*config).clone(); let config = (*config).clone();
let testfile = testfile.to_path_buf(); let testfile = testfile.to_path_buf();
test::DynTestFn(Thunk::new(move || { test::DynTestFn(Box::new(move || {
runtest::run(config, &testfile) runtest::run(config, &testfile)
})) }))
} }

View File

@ -393,28 +393,25 @@ impl<'a, 'b> From<&'b str> for Box<Error + Send + 'a> {
/// } /// }
/// } /// }
/// ``` /// ```
#[cfg(not(stage0))]
#[rustc_paren_sugar] #[rustc_paren_sugar]
#[unstable(feature = "core", reason = "Newly introduced")] #[unstable(feature = "core", reason = "Newly introduced")]
pub trait FnBox<A> { pub trait FnBox<A> {
type Output; type Output;
extern "rust-call" fn call_box(self: Box<Self>, args: A) -> Self::Output; fn call_box(self: Box<Self>, args: A) -> Self::Output;
} }
#[cfg(not(stage0))]
impl<A,F> FnBox<A> for F impl<A,F> FnBox<A> for F
where F: FnOnce<A> where F: FnOnce<A>
{ {
type Output = F::Output; type Output = F::Output;
extern "rust-call" fn call_box(self: Box<F>, args: A) -> F::Output { fn call_box(self: Box<F>, args: A) -> F::Output {
self.call_once(args) self.call_once(args)
} }
} }
#[cfg(not(stage0))] impl<'a,A,R> FnOnce<A> for Box<FnBox<A,Output=R>+'a> {
impl<A,R> FnOnce<A> for Box<FnBox<A,Output=R>> {
type Output = R; type Output = R;
extern "rust-call" fn call_once(self, args: A) -> R { extern "rust-call" fn call_once(self, args: A) -> R {
@ -422,8 +419,7 @@ impl<A,R> FnOnce<A> for Box<FnBox<A,Output=R>> {
} }
} }
#[cfg(not(stage0))] impl<'a,A,R> FnOnce<A> for Box<FnBox<A,Output=R>+Send+'a> {
impl<A,R> FnOnce<A> for Box<FnBox<A,Output=R>+Send> {
type Output = R; type Output = R;
extern "rust-call" fn call_once(self, args: A) -> R { extern "rust-call" fn call_once(self, args: A) -> R {

View File

@ -19,7 +19,6 @@ use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::str; use std::str;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thunk::Thunk;
use testing; use testing;
use rustc_lint; use rustc_lint;
@ -366,7 +365,7 @@ impl Collector {
ignore: should_ignore, ignore: should_ignore,
should_panic: testing::ShouldPanic::No, // compiler failures are test failures should_panic: testing::ShouldPanic::No, // compiler failures are test failures
}, },
testfn: testing::DynTestFn(Thunk::new(move|| { testfn: testing::DynTestFn(Box::new(move|| {
runtest(&test, runtest(&test,
&cratename, &cratename,
libs, libs,

View File

@ -64,7 +64,7 @@ pub fn cleanup() {
if queue as usize != 0 { if queue as usize != 0 {
let queue: Box<Queue> = Box::from_raw(queue); let queue: Box<Queue> = Box::from_raw(queue);
for to_run in *queue { for to_run in *queue {
to_run.invoke(()); to_run();
} }
} }
} }

View File

@ -21,7 +21,6 @@
use prelude::v1::*; use prelude::v1::*;
use sys; use sys;
use thunk::Thunk;
use usize; use usize;
// Reexport some of our utilities which are expected by other crates. // Reexport some of our utilities which are expected by other crates.
@ -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 /// that the closure could not be registered, meaning that it is not scheduled
/// to be rune. /// to be rune.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> { 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. /// One-time runtime cleanup.

View File

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

View File

@ -25,6 +25,7 @@ pub fn start_thread(main: *mut libc::c_void) {
unsafe { unsafe {
stack::record_os_managed_stack_bounds(0, usize::MAX); stack::record_os_managed_stack_bounds(0, usize::MAX);
let _handler = stack_overflow::Handler::new(); 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();
} }
} }

View File

@ -257,7 +257,7 @@ impl Builder {
pub fn spawn<F>(self, f: F) -> io::Result<JoinHandle> where pub fn spawn<F>(self, f: F) -> io::Result<JoinHandle> where
F: FnOnce(), F: Send + 'static 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 /// Spawn a new child thread that must be joined within a given
@ -279,7 +279,7 @@ impl Builder {
pub fn scoped<'a, T, F>(self, f: F) -> io::Result<JoinGuard<'a, T>> where pub fn scoped<'a, T, F>(self, f: F) -> io::Result<JoinGuard<'a, T>> where
T: Send + 'a, F: FnOnce() -> T, F: Send + 'a 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 } JoinGuard { inner: inner, _marker: PhantomData }
}) })
} }
@ -315,7 +315,7 @@ impl Builder {
thread_info::set(imp::guard::current(), their_thread); thread_info::set(imp::guard::current(), their_thread);
} }
let mut output = None; let mut output: Option<T> = None;
let try_result = { let try_result = {
let ptr = &mut output; let ptr = &mut output;
@ -327,7 +327,11 @@ impl Builder {
// 'unwinding' flag in the thread itself. For these reasons, // 'unwinding' flag in the thread itself. For these reasons,
// this unsafety should be ok. // this unsafety should be ok.
unsafe { unsafe {
unwind::try(move || *ptr = Some(f.invoke(()))) unwind::try(move || {
let f: Thunk<(), T> = f;
let v: T = f();
*ptr = Some(v)
})
} }
}; };
unsafe { unsafe {
@ -340,7 +344,7 @@ impl Builder {
}; };
Ok(JoinInner { 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, thread: my_thread,
packet: my_packet, packet: my_packet,
joined: false, joined: false,
@ -820,7 +824,7 @@ mod test {
let x: Box<_> = box 1; let x: Box<_> = box 1;
let x_in_parent = (&*x) as *const i32 as usize; 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; let x_in_child = (&*x) as *const i32 as usize;
tx.send(x_in_child).unwrap(); tx.send(x_in_child).unwrap();
})); }));
@ -832,7 +836,7 @@ mod test {
#[test] #[test]
fn test_avoid_copying_the_body_spawn() { fn test_avoid_copying_the_body_spawn() {
avoid_copying_the_body(|v| { avoid_copying_the_body(|v| {
thread::spawn(move || v.invoke(())); thread::spawn(move || v());
}); });
} }
@ -840,7 +844,7 @@ mod test {
fn test_avoid_copying_the_body_thread_spawn() { fn test_avoid_copying_the_body_thread_spawn() {
avoid_copying_the_body(|f| { avoid_copying_the_body(|f| {
thread::spawn(move|| { thread::spawn(move|| {
f.invoke(()); f();
}); });
}) })
} }
@ -849,7 +853,7 @@ mod test {
fn test_avoid_copying_the_body_join() { fn test_avoid_copying_the_body_join() {
avoid_copying_the_body(|f| { avoid_copying_the_body(|f| {
let _ = thread::spawn(move|| { let _ = thread::spawn(move|| {
f.invoke(()) f()
}).join(); }).join();
}) })
} }
@ -862,13 +866,13 @@ mod test {
// valgrind-friendly. try this at home, instead..!) // valgrind-friendly. try this at home, instead..!)
const GENERATIONS: u32 = 16; const GENERATIONS: u32 = 16;
fn child_no(x: u32) -> Thunk<'static> { fn child_no(x: u32) -> Thunk<'static> {
return Thunk::new(move|| { return Box::new(move|| {
if x < GENERATIONS { 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] #[test]

View File

@ -12,45 +12,9 @@
#![allow(missing_docs)] #![allow(missing_docs)]
#![unstable(feature = "std_misc")] #![unstable(feature = "std_misc")]
use alloc::boxed::Box; use alloc::boxed::{Box, FnBox};
use core::marker::Send; use core::marker::Send;
use core::ops::FnOnce;
pub struct Thunk<'a, A=(),R=()> { pub type Thunk<'a, A=(), R=()> =
invoke: Box<Invoke<A,R>+Send + 'a>, 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)
}
}

View File

@ -62,6 +62,7 @@ use self::OutputLocation::*;
use stats::Stats; use stats::Stats;
use getopts::{OptGroup, optflag, optopt}; use getopts::{OptGroup, optflag, optopt};
use serialize::Encodable; use serialize::Encodable;
use std::boxed::FnBox;
use term::Terminal; use term::Terminal;
use term::color::{Color, RED, YELLOW, GREEN, CYAN}; use term::color::{Color, RED, YELLOW, GREEN, CYAN};
@ -79,7 +80,7 @@ use std::path::PathBuf;
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::thunk::{Thunk, Invoke}; use std::thunk::Thunk;
use std::time::Duration; use std::time::Duration;
// to be used by rustc to compile tests in libtest // to be used by rustc to compile tests in libtest
@ -158,7 +159,7 @@ pub enum TestFn {
StaticBenchFn(fn(&mut Bencher)), StaticBenchFn(fn(&mut Bencher)),
StaticMetricFn(fn(&mut MetricMap)), StaticMetricFn(fn(&mut MetricMap)),
DynTestFn(Thunk<'static>), DynTestFn(Thunk<'static>),
DynMetricFn(Box<for<'a> Invoke<&'a mut MetricMap>+'static>), DynMetricFn(Box<FnBox(&mut MetricMap)+Send>),
DynBenchFn(Box<TDynBenchFn+'static>) DynBenchFn(Box<TDynBenchFn+'static>)
} }
@ -936,7 +937,7 @@ pub fn run_test(opts: &TestOpts,
io::set_print(box Sink(data2.clone())); io::set_print(box Sink(data2.clone()));
io::set_panic(box Sink(data2)); io::set_panic(box Sink(data2));
} }
testfn.invoke(()) testfn()
}).unwrap(); }).unwrap();
let test_result = calc_result(&desc, result_guard.join()); let test_result = calc_result(&desc, result_guard.join());
let stdout = data.lock().unwrap().to_vec(); let stdout = data.lock().unwrap().to_vec();
@ -957,7 +958,7 @@ pub fn run_test(opts: &TestOpts,
} }
DynMetricFn(f) => { DynMetricFn(f) => {
let mut mm = MetricMap::new(); let mut mm = MetricMap::new();
f.invoke(&mut mm); f.call_box((&mut mm,)); // TODO unfortunate
monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap(); monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
return; return;
} }
@ -969,7 +970,7 @@ pub fn run_test(opts: &TestOpts,
} }
DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f), DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
Thunk::new(move|| f())) Box::new(move|| f()))
} }
} }
@ -1185,7 +1186,7 @@ mod tests {
ignore: true, ignore: true,
should_panic: ShouldPanic::No, should_panic: ShouldPanic::No,
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1202,7 +1203,7 @@ mod tests {
ignore: true, ignore: true,
should_panic: ShouldPanic::No, should_panic: ShouldPanic::No,
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1219,7 +1220,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::Yes(None) should_panic: ShouldPanic::Yes(None)
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1236,7 +1237,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::Yes(Some("error message")) should_panic: ShouldPanic::Yes(Some("error message"))
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1253,7 +1254,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::Yes(Some("foobar")) should_panic: ShouldPanic::Yes(Some("foobar"))
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1270,7 +1271,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::Yes(None) should_panic: ShouldPanic::Yes(None)
}, },
testfn: DynTestFn(Thunk::new(move|| f())), testfn: DynTestFn(Box::new(move|| f())),
}; };
let (tx, rx) = channel(); let (tx, rx) = channel();
run_test(&TestOpts::new(), false, desc, tx); run_test(&TestOpts::new(), false, desc, tx);
@ -1306,7 +1307,7 @@ mod tests {
ignore: true, ignore: true,
should_panic: ShouldPanic::No, should_panic: ShouldPanic::No,
}, },
testfn: DynTestFn(Thunk::new(move|| {})), testfn: DynTestFn(Box::new(move|| {})),
}, },
TestDescAndFn { TestDescAndFn {
desc: TestDesc { desc: TestDesc {
@ -1314,7 +1315,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::No, should_panic: ShouldPanic::No,
}, },
testfn: DynTestFn(Thunk::new(move|| {})), testfn: DynTestFn(Box::new(move|| {})),
}); });
let filtered = filter_tests(&opts, tests); let filtered = filter_tests(&opts, tests);
@ -1350,7 +1351,7 @@ mod tests {
ignore: false, ignore: false,
should_panic: ShouldPanic::No, should_panic: ShouldPanic::No,
}, },
testfn: DynTestFn(Thunk::new(testfn)), testfn: DynTestFn(Box::new(testfn)),
}; };
tests.push(test); tests.push(test);
} }

View File

@ -25,7 +25,7 @@ fn test(slot: &mut Option<Thunk<(),Thunk>>) -> () {
let a = slot.take(); let a = slot.take();
let _a = match a { let _a = match a {
// `{let .. a(); }` would break // `{let .. a(); }` would break
Some(a) => { let _a = a.invoke(()); }, Some(a) => { let _a = a(); },
None => (), None => (),
}; };
} }

View File

@ -23,5 +23,5 @@ use std::thunk::Thunk;
pub fn main() { pub fn main() {
let mut x = 1; let mut x = 1;
let _thunk = Thunk::new(move|| { x = 2; }); let _thunk = Box::new(move|| { x = 2; });
} }

View File

@ -12,10 +12,10 @@
use std::thunk::Thunk; use std::thunk::Thunk;
fn action(cb: Thunk<usize, usize>) -> usize { fn action(cb: Thunk<(usize,), usize>) -> usize {
cb.invoke(1) cb(1)
} }
pub fn main() { pub fn main() {
println!("num: {}", action(Thunk::with_arg(move |u| u))); println!("num: {}", action(Box::new(move |u| u)));
} }

View File

@ -16,13 +16,13 @@ use std::thunk::Thunk;
pub trait Promisable: Send + Sync {} pub trait Promisable: Send + Sync {}
impl<T: Send + Sync> Promisable for T {} impl<T: Send + Sync> Promisable for T {}
pub fn propagate<'a, T, E, F, G>(action: F) -> Thunk<'a,Result<T, E>, Result<T, E>> pub fn propagate<'a, T, E, F, G>(action: F) -> Thunk<'a, (Result<T, E>,), Result<T, E>>
where where
T: Promisable + Clone + 'a, T: Promisable + Clone + 'a,
E: Promisable + Clone + 'a, E: Promisable + Clone + 'a,
F: FnOnce(&T) -> Result<T, E> + Send + 'a, F: FnOnce(&T) -> Result<T, E> + Send + 'a,
G: FnOnce(Result<T, E>) -> Result<T, E> + 'a { G: FnOnce(Result<T, E>) -> Result<T, E> + 'a {
Thunk::with_arg(move |result: Result<T, E>| { Box::new(move |result: Result<T, E>| {
match result { match result {
Ok(ref t) => action(t), Ok(ref t) => action(t),
Err(ref e) => Err(e.clone()), Err(ref e) => Err(e.clone()),

View File

@ -18,11 +18,11 @@ use std::thunk::Thunk;
static generations: usize = 1024+256+128+49; static generations: usize = 1024+256+128+49;
fn spawn(f: Thunk<'static>) { fn spawn(f: Thunk<'static>) {
Builder::new().stack_size(32 * 1024).spawn(move|| f.invoke(())); Builder::new().stack_size(32 * 1024).spawn(move|| f());
} }
fn child_no(x: usize) -> Thunk<'static> { fn child_no(x: usize) -> Thunk<'static> {
Thunk::new(move|| { Box::new(move|| {
if x < generations { if x < generations {
spawn(child_no(x+1)); spawn(child_no(x+1));
} }

View File

@ -13,7 +13,6 @@
use std::thread; use std::thread;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::thunk::Invoke;
type RingBuffer = Vec<f64> ; type RingBuffer = Vec<f64> ;
type SamplesFn = Box<FnMut(&RingBuffer) + Send>; type SamplesFn = Box<FnMut(&RingBuffer) + Send>;