Skip to content

Commit 5d6dbf3

Browse files
committed
Improve the rt::thread module
* Added doc comments explaining what all public functionality does. * Added the ability to spawn a detached thread * Added the ability for the procs to return a value in 'join'
1 parent ed86b48 commit 5d6dbf3

File tree

4 files changed

+154
-62
lines changed

4 files changed

+154
-62
lines changed

src/libstd/rt/local_ptr.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void;
4141
#[cfg(stage0)]
4242
#[cfg(windows)]
4343
static mut RT_TLS_KEY: tls::Key = -1;
44+
#[cfg(stage0)]
45+
#[cfg(windows)]
4446
static mut tls_lock: Mutex = MUTEX_INIT;
4547
static mut tls_initialized: bool = false;
4648

@@ -60,7 +62,11 @@ pub fn init_tls_key() {
6062
}
6163

6264
#[cfg(not(stage0), not(windows))]
63-
pub fn init_tls_key() {}
65+
pub fn init_tls_key() {
66+
unsafe {
67+
tls_initialized = true;
68+
}
69+
}
6470

6571
#[cfg(windows)]
6672
pub unsafe fn cleanup() {
@@ -76,7 +82,6 @@ pub unsafe fn cleanup() {
7682
#[cfg(not(windows))]
7783
pub unsafe fn cleanup() {
7884
assert!(tls_initialized);
79-
tls_lock.destroy();
8085
tls_initialized = false;
8186
}
8287

src/libstd/rt/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ pub fn spawntask_try(f: proc()) -> Result<(),()> {
336336
}
337337

338338
/// Spawn a new task in a new scheduler and return a thread handle.
339-
pub fn spawntask_thread(f: proc()) -> Thread {
339+
pub fn spawntask_thread(f: proc()) -> Thread<()> {
340340

341341
let f = Cell::new(f);
342342

src/libstd/rt/thread.rs

Lines changed: 145 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,126 +8,213 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
//! Native os-thread management
12+
//!
13+
//! This modules contains bindings necessary for managing OS-level threads.
14+
//! These functions operate outside of the rust runtime, creating threads
15+
//! which are not used for scheduling in any way.
16+
1117
#[allow(non_camel_case_types)];
1218

1319
use cast;
20+
use kinds::Send;
1421
use libc;
1522
use ops::Drop;
16-
use uint;
23+
use option::{Option, Some, None};
1724
use ptr;
25+
use uint;
1826

1927
#[cfg(windows)]
2028
use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T,
2129
LPVOID, DWORD, LPDWORD, HANDLE};
2230

2331
#[cfg(windows)] type rust_thread = HANDLE;
2432
#[cfg(unix)] type rust_thread = libc::pthread_t;
33+
#[cfg(windows)] type rust_thread_return = DWORD;
34+
#[cfg(unix)] type rust_thread_return = *libc::c_void;
2535

26-
pub struct Thread {
36+
type StartFn = extern "C" fn(*libc::c_void) -> rust_thread_return;
37+
38+
/// This struct represents a native thread's state. This is used to join on an
39+
/// existing thread created in the join-able state.
40+
pub struct Thread<T> {
2741
priv native: rust_thread,
28-
priv joined: bool
42+
priv joined: bool,
43+
priv packet: ~Option<T>,
2944
}
3045

3146
static DEFAULT_STACK_SIZE: libc::size_t = 1024*1024;
3247

33-
#[cfg(windows)] type rust_thread_return = DWORD;
34-
#[cfg(unix)] type rust_thread_return = *libc::c_void;
48+
// This is the starting point of rust os threads. The first thing we do
49+
// is make sure that we don't trigger __morestack (also why this has a
50+
// no_split_stack annotation), and then we extract the main function
51+
// and invoke it.
52+
#[no_split_stack]
53+
extern fn thread_start(main: *libc::c_void) -> rust_thread_return {
54+
use rt::context;
55+
unsafe {
56+
context::record_stack_bounds(0, uint::max_value);
57+
let f: ~proc() = cast::transmute(main);
58+
(*f)();
59+
cast::transmute(0 as rust_thread_return)
60+
}
61+
}
3562

36-
impl Thread {
37-
38-
pub fn start(main: proc()) -> Thread {
39-
// This is the starting point of rust os threads. The first thing we do
40-
// is make sure that we don't trigger __morestack (also why this has a
41-
// no_split_stack annotation), and then we extract the main function
42-
// and invoke it.
43-
#[no_split_stack]
44-
extern "C" fn thread_start(trampoline: *libc::c_void) -> rust_thread_return {
45-
use rt::context;
46-
unsafe {
47-
context::record_stack_bounds(0, uint::max_value);
48-
let f: ~proc() = cast::transmute(trampoline);
49-
(*f)();
50-
}
51-
unsafe { cast::transmute(0 as rust_thread_return) }
52-
}
63+
// There are two impl blocks b/c if T were specified at the top then it's just a
64+
// pain to specify a type parameter on Thread::spawn (which doesn't need the
65+
// type parameter).
66+
impl Thread<()> {
67+
68+
/// Starts execution of a new OS thread.
69+
///
70+
/// This function will not wait for the thread to join, but a handle to the
71+
/// thread will be returned.
72+
///
73+
/// Note that the handle returned is used to acquire the return value of the
74+
/// procedure `main`. The `join` function will wait for the thread to finish
75+
/// and return the value that `main` generated.
76+
///
77+
/// Also note that the `Thread` returned will *always* wait for the thread
78+
/// to finish executing. This means that even if `join` is not explicitly
79+
/// called, when the `Thread` falls out of scope its destructor will block
80+
/// waiting for the OS thread.
81+
pub fn start<T: Send>(main: proc() -> T) -> Thread<T> {
82+
83+
// We need the address of the packet to fill in to be stable so when
84+
// `main` fills it in it's still valid, so allocate an extra ~ box to do
85+
// so.
86+
let packet = ~None;
87+
let packet2: *mut Option<T> = unsafe {
88+
*cast::transmute::<&~Option<T>, **mut Option<T>>(&packet)
89+
};
90+
let main: proc() = proc() unsafe { *packet2 = Some(main()); };
91+
let native = unsafe { native_thread_create(~main) };
5392

54-
let native = native_thread_create(thread_start, ~main);
5593
Thread {
5694
native: native,
5795
joined: false,
96+
packet: packet,
5897
}
5998
}
6099

61-
pub fn join(mut self) {
100+
/// This will spawn a new thread, but it will not wait for the thread to
101+
/// finish, nor is it possible to wait for the thread to finish.
102+
///
103+
/// This corresponds to creating threads in the 'detached' state on unix
104+
/// systems. Note that platforms may not keep the main program alive even if
105+
/// there are detached thread still running around.
106+
pub fn spawn(main: proc()) {
107+
unsafe {
108+
let handle = native_thread_create(~main);
109+
native_thread_detach(handle);
110+
}
111+
}
112+
}
113+
114+
impl<T: Send> Thread<T> {
115+
/// Wait for this thread to finish, returning the result of the thread's
116+
/// calculation.
117+
pub fn join(mut self) -> T {
62118
assert!(!self.joined);
63-
native_thread_join(self.native);
119+
unsafe { native_thread_join(self.native) };
64120
self.joined = true;
121+
assert!(self.packet.is_some());
122+
self.packet.take_unwrap()
65123
}
66124
}
67125

68-
#[cfg(windows)]
69-
fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return,
70-
tramp: ~proc()) -> rust_thread {
71-
unsafe {
72-
let ptr: *mut libc::c_void = cast::transmute(tramp);
73-
CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start, ptr, 0, ptr::mut_null())
126+
#[unsafe_destructor]
127+
impl<T: Send> Drop for Thread<T> {
128+
fn drop(&mut self) {
129+
// This is required for correctness. If this is not done then the thread
130+
// would fill in a return box which no longer exists.
131+
if !self.joined {
132+
unsafe { native_thread_join(self.native) };
133+
}
74134
}
75135
}
76136

77137
#[cfg(windows)]
78-
fn native_thread_join(native: rust_thread) {
138+
unsafe fn native_thread_create(p: ~proc()) -> rust_thread {
139+
let arg: *mut libc::c_void = cast::transmute(p);
140+
CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start,
141+
arg, 0, ptr::mut_null())
142+
}
143+
144+
#[cfg(windows)]
145+
unsafe fn native_thread_join(native: rust_thread) {
79146
use libc::consts::os::extra::INFINITE;
80-
unsafe { WaitForSingleObject(native, INFINITE); }
147+
WaitForSingleObject(native, INFINITE);
148+
}
149+
150+
#[cfg(windows)]
151+
unsafe fn native_thread_detach(native: rust_thread) {
152+
assert!(libc::CloseHandle(native) != 0);
81153
}
82154

83155
#[cfg(unix)]
84-
fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return,
85-
tramp: ~proc()) -> rust_thread {
156+
unsafe fn native_thread_create(p: ~proc()) -> rust_thread {
86157
use unstable::intrinsics;
87-
let mut native: libc::pthread_t = unsafe { intrinsics::uninit() };
88-
89-
unsafe {
90-
use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE;
158+
use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE;
91159

92-
let mut attr: libc::pthread_attr_t = intrinsics::uninit();
93-
assert!(pthread_attr_init(&mut attr) == 0);
94-
assert!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE) == 0);
95-
assert!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE) == 0);
160+
let mut native: libc::pthread_t = intrinsics::uninit();
161+
let mut attr: libc::pthread_attr_t = intrinsics::uninit();
162+
assert_eq!(pthread_attr_init(&mut attr), 0);
163+
assert_eq!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE), 0);
164+
assert_eq!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE), 0);
96165

97-
let ptr: *libc::c_void = cast::transmute(tramp);
98-
assert!(pthread_create(&mut native, &attr, thread_start, ptr) == 0);
99-
}
166+
let arg: *libc::c_void = cast::transmute(p);
167+
assert_eq!(pthread_create(&mut native, &attr, thread_start, arg), 0);
100168
native
101169
}
102170

103171
#[cfg(unix)]
104-
fn native_thread_join(native: rust_thread) {
105-
unsafe { assert!(pthread_join(native, ptr::null()) == 0) }
172+
unsafe fn native_thread_join(native: rust_thread) {
173+
assert_eq!(pthread_join(native, ptr::null()), 0);
106174
}
107175

108-
impl Drop for Thread {
109-
fn drop(&mut self) {
110-
assert!(self.joined);
111-
}
176+
#[cfg(unix)]
177+
fn native_thread_detach(native: rust_thread) {
178+
unsafe { assert_eq!(pthread_detach(native), 0) }
112179
}
113180

114181
#[cfg(windows)]
115182
extern "system" {
116-
fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T,
117-
lpStartAddress: extern "C" fn(*libc::c_void) -> rust_thread_return,
118-
lpParameter: LPVOID, dwCreationFlags: DWORD, lpThreadId: LPDWORD) -> HANDLE;
183+
fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
184+
dwStackSize: SIZE_T,
185+
lpStartAddress: StartFn,
186+
lpParameter: LPVOID,
187+
dwCreationFlags: DWORD,
188+
lpThreadId: LPDWORD) -> HANDLE;
119189
fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
120190
}
121191

122192
#[cfg(unix)]
123193
extern {
124-
fn pthread_create(native: *mut libc::pthread_t, attr: *libc::pthread_attr_t,
125-
f: extern "C" fn(*libc::c_void) -> rust_thread_return,
194+
fn pthread_create(native: *mut libc::pthread_t,
195+
attr: *libc::pthread_attr_t,
196+
f: StartFn,
126197
value: *libc::c_void) -> libc::c_int;
127-
fn pthread_join(native: libc::pthread_t, value: **libc::c_void) -> libc::c_int;
198+
fn pthread_join(native: libc::pthread_t,
199+
value: **libc::c_void) -> libc::c_int;
128200
fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
129201
fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
130202
stack_size: libc::size_t) -> libc::c_int;
131203
fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
132204
state: libc::c_int) -> libc::c_int;
205+
fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
206+
}
207+
208+
#[cfg(test)]
209+
mod tests {
210+
use super::Thread;
211+
212+
#[test]
213+
fn smoke() { do Thread::start {}.join(); }
214+
215+
#[test]
216+
fn data() { assert_eq!(do Thread::start { 1 }.join(), 1); }
217+
218+
#[test]
219+
fn detached() { do Thread::spawn {} }
133220
}

src/libstd/task/spawn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ pub fn spawn_raw(mut opts: TaskOpts, f: proc()) {
139139
let join_task = do Task::build_child(None) {
140140
debug!("running join task");
141141
let thread_port = thread_port_cell.take();
142-
let thread: Thread = thread_port.recv();
142+
let thread: Thread<()> = thread_port.recv();
143143
thread.join();
144144
};
145145

0 commit comments

Comments
 (0)