Skip to content

Commit d613381

Browse files
committed
Rewrite threading infrastructure, introducing Thunk to represent
boxed `FnOnce` closures.
1 parent 10ac5b7 commit d613381

File tree

11 files changed

+225
-124
lines changed

11 files changed

+225
-124
lines changed

src/librustrt/at_exit_imp.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ use alloc::boxed::Box;
1818
use collections::vec::Vec;
1919
use core::atomic;
2020
use core::mem;
21+
use thunk::{Thunk};
2122

2223
use exclusive::Exclusive;
2324

24-
type Queue = Exclusive<Vec<proc():Send>>;
25+
type Queue = Exclusive<Vec<Thunk>>;
2526

2627
static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT;
2728
static RUNNING: atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL;
@@ -34,7 +35,7 @@ pub fn init() {
3435
}
3536
}
3637

37-
pub fn push(f: proc():Send) {
38+
pub fn push(f: Thunk) {
3839
unsafe {
3940
// Note that the check against 0 for the queue pointer is not atomic at
4041
// all with respect to `run`, meaning that this could theoretically be a
@@ -59,6 +60,6 @@ pub fn run() {
5960
};
6061

6162
for to_run in cur.into_iter() {
62-
to_run();
63+
to_run.invoke(());
6364
}
6465
}

src/librustrt/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ mod thread_local_storage;
4646
mod util;
4747
mod libunwind;
4848
mod stack_overflow;
49+
pub mod thunk;
4950

5051
pub mod args;
5152
pub mod bookkeeping;
@@ -95,8 +96,8 @@ pub fn init(argc: int, argv: *const *const u8) {
9596
///
9697
/// It is forbidden for procedures to register more `at_exit` handlers when they
9798
/// are running, and doing so will lead to a process abort.
98-
pub fn at_exit(f: proc():Send) {
99-
at_exit_imp::push(f);
99+
pub fn at_exit<F:FnOnce()+Send>(f: F) {
100+
at_exit_imp::push(thunk::Thunk::new(f));
100101
}
101102

102103
/// One-time runtime cleanup.

src/librustrt/task.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use core::any::Any;
2121
use core::atomic::{AtomicUint, SeqCst};
2222
use core::iter::{IteratorExt, Take};
2323
use core::kinds::marker;
24+
use core::ops::FnOnce;
2425
use core::mem;
2526
use core::ops::FnMut;
2627
use core::prelude::{Clone, Drop, Err, Iterator, None, Ok, Option, Send, Some};
@@ -34,6 +35,7 @@ use stack;
3435
use unwind;
3536
use unwind::Unwinder;
3637
use collections::str::SendStr;
38+
use thunk::Thunk;
3739

3840
/// State associated with Rust tasks.
3941
///
@@ -67,7 +69,7 @@ enum TaskState {
6769

6870
pub struct TaskOpts {
6971
/// Invoke this procedure with the result of the task when it finishes.
70-
pub on_exit: Option<proc(Result): Send>,
72+
pub on_exit: Option<Thunk<Result>>,
7173
/// A name for the task-to-be, for identification in panic messages
7274
pub name: Option<SendStr>,
7375
/// The size of the stack for the spawned task
@@ -92,7 +94,7 @@ pub enum BlockedTask {
9294

9395
/// Per-task state related to task death, killing, panic, etc.
9496
pub struct Death {
95-
pub on_exit: Option<proc(Result):Send>,
97+
pub on_exit: Option<Thunk<Result>>,
9698
marker: marker::NoCopy,
9799
}
98100

@@ -116,7 +118,13 @@ impl Task {
116118
}
117119
}
118120

119-
pub fn spawn(opts: TaskOpts, f: proc():Send) {
121+
pub fn spawn<F>(opts: TaskOpts, f: F)
122+
where F : FnOnce(), F : Send
123+
{
124+
Task::spawn_thunk(opts, Thunk::new(f))
125+
}
126+
127+
fn spawn_thunk(opts: TaskOpts, f: Thunk) {
120128
let TaskOpts { name, stack_size, on_exit } = opts;
121129

122130
let mut task = box Task::new(None, None);
@@ -138,7 +146,7 @@ impl Task {
138146
// because by the time that this function is executing we've already
139147
// consumed at least a little bit of stack (we don't know the exact byte
140148
// address at which our stack started).
141-
Thread::spawn_stack(stack, proc() {
149+
Thread::spawn_stack(stack, move|| {
142150
let something_around_the_top_of_the_stack = 1;
143151
let addr = &something_around_the_top_of_the_stack as *const int;
144152
let my_stack = addr as uint;
@@ -150,7 +158,7 @@ impl Task {
150158
task.stack_bounds = (my_stack - stack + 1024, my_stack);
151159

152160
let mut f = Some(f);
153-
drop(task.run(|| { f.take().unwrap()() }).destroy());
161+
drop(task.run(|| { f.take().unwrap().invoke(()) }).destroy());
154162
drop(token);
155163
})
156164
}
@@ -241,7 +249,7 @@ impl Task {
241249
// reconsideration to whether it's a reasonable thing to let a
242250
// task to do or not.
243251
match what_to_do {
244-
Some(f) => { f(result) }
252+
Some(f) => { f.invoke(result) }
245253
None => { drop(result) }
246254
}
247255

@@ -500,14 +508,13 @@ mod test {
500508
use super::*;
501509
use std::prelude::*;
502510
use std::task;
503-
use unwind;
504511

505512
#[test]
506513
fn unwind() {
507-
let result = task::try(proc()());
514+
let result = task::try(move|| ());
508515
rtdebug!("trying first assert");
509516
assert!(result.is_ok());
510-
let result = task::try::<()>(proc() panic!());
517+
let result = task::try(move|| -> () panic!());
511518
rtdebug!("trying second assert");
512519
assert!(result.is_err());
513520
}

src/librustrt/thread.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use alloc::boxed::Box;
2222
use core::mem;
2323
use core::uint;
2424
use libc;
25+
use thunk::{Thunk};
2526

2627
use stack;
2728
use stack_overflow;
@@ -60,8 +61,8 @@ fn start_thread(main: *mut libc::c_void) -> imp::rust_thread_return {
6061
unsafe {
6162
stack::record_os_managed_stack_bounds(0, uint::MAX);
6263
let handler = stack_overflow::Handler::new();
63-
let f: Box<proc()> = mem::transmute(main);
64-
(*f)();
64+
let f: Box<Thunk> = mem::transmute(main);
65+
f.invoke(());
6566
drop(handler);
6667
mem::transmute(0 as imp::rust_thread_return)
6768
}
@@ -113,23 +114,29 @@ impl Thread<()> {
113114
/// to finish executing. This means that even if `join` is not explicitly
114115
/// called, when the `Thread` falls out of scope its destructor will block
115116
/// waiting for the OS thread.
116-
pub fn start<T: Send>(main: proc():Send -> T) -> Thread<T> {
117+
pub fn start<T,F>(main: F) -> Thread<T>
118+
where T:Send, F:FnOnce() -> T, F:Send
119+
{
117120
Thread::start_stack(DEFAULT_STACK_SIZE, main)
118121
}
119122

120123
/// Performs the same functionality as `start`, but specifies an explicit
121124
/// stack size for the new thread.
122-
pub fn start_stack<T: Send>(stack: uint, main: proc():Send -> T) -> Thread<T> {
123-
125+
pub fn start_stack<T, F>(stack: uint, main: F) -> Thread<T>
126+
where T:Send, F:FnOnce() -> T, F:Send
127+
{
124128
// We need the address of the packet to fill in to be stable so when
125129
// `main` fills it in it's still valid, so allocate an extra box to do
126130
// so.
127131
let packet = box None;
128132
let packet2: *mut Option<T> = unsafe {
129133
*mem::transmute::<&Box<Option<T>>, *const *mut Option<T>>(&packet)
130134
};
131-
let main = proc() unsafe { *packet2 = Some(main()); };
132-
let native = unsafe { imp::create(stack, box main) };
135+
let native = unsafe {
136+
imp::create(stack, Thunk::new(move |:| {
137+
*packet2 = Some(main.call_once(()));
138+
}))
139+
};
133140

134141
Thread {
135142
native: native,
@@ -144,15 +151,19 @@ impl Thread<()> {
144151
/// This corresponds to creating threads in the 'detached' state on unix
145152
/// systems. Note that platforms may not keep the main program alive even if
146153
/// there are detached thread still running around.
147-
pub fn spawn(main: proc():Send) {
154+
pub fn spawn<F>(main: F)
155+
where F : FnOnce() + Send
156+
{
148157
Thread::spawn_stack(DEFAULT_STACK_SIZE, main)
149158
}
150159

151160
/// Performs the same functionality as `spawn`, but explicitly specifies a
152161
/// stack size for the new thread.
153-
pub fn spawn_stack(stack: uint, main: proc():Send) {
162+
pub fn spawn_stack<F>(stack: uint, main: F)
163+
where F : FnOnce() + Send
164+
{
154165
unsafe {
155-
let handle = imp::create(stack, box main);
166+
let handle = imp::create(stack, Thunk::new(main));
156167
imp::detach(handle);
157168
}
158169
}
@@ -190,8 +201,6 @@ impl<T: Send> Drop for Thread<T> {
190201
#[cfg(windows)]
191202
#[allow(non_snake_case)]
192203
mod imp {
193-
use core::prelude::*;
194-
195204
use alloc::boxed::Box;
196205
use core::cmp;
197206
use core::mem;
@@ -200,6 +209,7 @@ mod imp {
200209
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL,
201210
LPVOID, DWORD, LPDWORD, HANDLE};
202211
use stack::RED_ZONE;
212+
use thunk::Thunk;
203213

204214
pub type rust_thread = HANDLE;
205215
pub type rust_thread_return = DWORD;
@@ -217,8 +227,9 @@ mod imp {
217227
}
218228
}
219229

220-
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
221-
let arg: *mut libc::c_void = mem::transmute(p);
230+
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
231+
let arg: *mut libc::c_void = mem::transmute(box p);
232+
222233
// FIXME On UNIX, we guard against stack sizes that are too small but
223234
// that's because pthreads enforces that stacks are at least
224235
// PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
@@ -234,7 +245,7 @@ mod imp {
234245

235246
if ret as uint == 0 {
236247
// be sure to not leak the closure
237-
let _p: Box<proc():Send> = mem::transmute(arg);
248+
let _p: Box<Thunk> = mem::transmute(arg);
238249
panic!("failed to spawn native thread: {}", ret);
239250
}
240251
return ret;
@@ -279,6 +290,7 @@ mod imp {
279290
use core::ptr;
280291
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
281292
use libc;
293+
use thunk::Thunk;
282294

283295
use stack::RED_ZONE;
284296

@@ -409,7 +421,7 @@ mod imp {
409421
}
410422
}
411423

412-
pub unsafe fn create(stack: uint, p: Box<proc():Send>) -> rust_thread {
424+
pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
413425
let mut native: libc::pthread_t = mem::zeroed();
414426
let mut attr: libc::pthread_attr_t = mem::zeroed();
415427
assert_eq!(pthread_attr_init(&mut attr), 0);
@@ -437,13 +449,13 @@ mod imp {
437449
},
438450
};
439451

440-
let arg: *mut libc::c_void = mem::transmute(p);
452+
let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
441453
let ret = pthread_create(&mut native, &attr, super::thread_start, arg);
442454
assert_eq!(pthread_attr_destroy(&mut attr), 0);
443455

444456
if ret != 0 {
445457
// be sure to not leak the closure
446-
let _p: Box<proc():Send> = mem::transmute(arg);
458+
let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
447459
panic!("failed to spawn native thread: {}", ret);
448460
}
449461
native
@@ -531,17 +543,17 @@ mod tests {
531543
use super::Thread;
532544

533545
#[test]
534-
fn smoke() { Thread::start(proc (){}).join(); }
546+
fn smoke() { Thread::start(move|| {}).join(); }
535547

536548
#[test]
537-
fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); }
549+
fn data() { assert_eq!(Thread::start(move|| { 1i }).join(), 1); }
538550

539551
#[test]
540-
fn detached() { Thread::spawn(proc () {}) }
552+
fn detached() { Thread::spawn(move|| {}) }
541553

542554
#[test]
543555
fn small_stacks() {
544-
assert_eq!(42i, Thread::start_stack(0, proc () 42i).join());
545-
assert_eq!(42i, Thread::start_stack(1, proc () 42i).join());
556+
assert_eq!(42i, Thread::start_stack(0, move|| 42i).join());
557+
assert_eq!(42i, Thread::start_stack(1, move|| 42i).join());
546558
}
547559
}

src/librustrt/thunk.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use alloc::boxed::Box;
12+
use core::kinds::Send;
13+
use core::ops::FnOnce;
14+
15+
pub struct Thunk<A=(),R=()> {
16+
invoke: Box<Invoke<A,R>+Send>
17+
}
18+
19+
impl<R> Thunk<(),R> {
20+
pub fn new<F>(func: F) -> Thunk<(),R>
21+
where F : FnOnce() -> R, F : Send
22+
{
23+
Thunk::with_arg(move|: ()| func())
24+
}
25+
}
26+
27+
impl<A,R> Thunk<A,R> {
28+
pub fn with_arg<F>(func: F) -> Thunk<A,R>
29+
where F : FnOnce(A) -> R, F : Send
30+
{
31+
Thunk {
32+
invoke: box func
33+
}
34+
}
35+
36+
pub fn invoke(self, arg: A) -> R {
37+
self.invoke.invoke(arg)
38+
}
39+
}
40+
41+
pub trait Invoke<A=(),R=()> {
42+
fn invoke(self: Box<Self>, arg: A) -> R;
43+
}
44+
45+
impl<A,R,F> Invoke<A,R> for F
46+
where F : FnOnce(A) -> R
47+
{
48+
fn invoke(self: Box<F>, arg: A) -> R {
49+
let f = *self;
50+
f(arg)
51+
}
52+
}

src/libstd/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ pub use rustrt::c_str;
171171

172172
pub use unicode::char;
173173

174+
pub use rustrt::thunk;
175+
174176
/* Exported macros */
175177

176178
pub mod macros;

0 commit comments

Comments
 (0)