about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-11-25 18:08:31 -0800
committerAlex Crichton <alex@alexcrichton.com>2013-11-27 09:53:48 -0800
commit5d6dbf3f262fabcb6cb920dd08be6f9d8df75d5c (patch)
tree8218728771f9eb50c20a621736c0dd4e682d15b3
parented86b48cc9a8349e8e99de5f013f68f1edff4121 (diff)
downloadrust-5d6dbf3f262fabcb6cb920dd08be6f9d8df75d5c.tar.gz
rust-5d6dbf3f262fabcb6cb920dd08be6f9d8df75d5c.zip
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'
-rw-r--r--src/libstd/rt/local_ptr.rs9
-rw-r--r--src/libstd/rt/test.rs2
-rw-r--r--src/libstd/rt/thread.rs203
-rw-r--r--src/libstd/task/spawn.rs2
4 files changed, 154 insertions, 62 deletions
diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs
index 86f0f643c99..6355de36d43 100644
--- a/src/libstd/rt/local_ptr.rs
+++ b/src/libstd/rt/local_ptr.rs
@@ -41,6 +41,8 @@ pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void;
 #[cfg(stage0)]
 #[cfg(windows)]
 static mut RT_TLS_KEY: tls::Key = -1;
+#[cfg(stage0)]
+#[cfg(windows)]
 static mut tls_lock: Mutex = MUTEX_INIT;
 static mut tls_initialized: bool = false;
 
@@ -60,7 +62,11 @@ pub fn init_tls_key() {
 }
 
 #[cfg(not(stage0), not(windows))]
-pub fn init_tls_key() {}
+pub fn init_tls_key() {
+    unsafe {
+        tls_initialized = true;
+    }
+}
 
 #[cfg(windows)]
 pub unsafe fn cleanup() {
@@ -76,7 +82,6 @@ pub unsafe fn cleanup() {
 #[cfg(not(windows))]
 pub unsafe fn cleanup() {
     assert!(tls_initialized);
-    tls_lock.destroy();
     tls_initialized = false;
 }
 
diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs
index 867d997e98d..943b76dd1a0 100644
--- a/src/libstd/rt/test.rs
+++ b/src/libstd/rt/test.rs
@@ -336,7 +336,7 @@ pub fn spawntask_try(f: proc()) -> Result<(),()> {
 }
 
 /// Spawn a new task in a new scheduler and return a thread handle.
-pub fn spawntask_thread(f: proc()) -> Thread {
+pub fn spawntask_thread(f: proc()) -> Thread<()> {
 
     let f = Cell::new(f);
 
diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs
index a0e66d2fd4e..9031147f8b1 100644
--- a/src/libstd/rt/thread.rs
+++ b/src/libstd/rt/thread.rs
@@ -8,13 +8,21 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! Native os-thread management
+//!
+//! This modules contains bindings necessary for managing OS-level threads.
+//! These functions operate outside of the rust runtime, creating threads
+//! which are not used for scheduling in any way.
+
 #[allow(non_camel_case_types)];
 
 use cast;
+use kinds::Send;
 use libc;
 use ops::Drop;
-use uint;
+use option::{Option, Some, None};
 use ptr;
+use uint;
 
 #[cfg(windows)]
 use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T,
@@ -22,112 +30,191 @@ use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T,
 
 #[cfg(windows)] type rust_thread = HANDLE;
 #[cfg(unix)] type rust_thread = libc::pthread_t;
+#[cfg(windows)] type rust_thread_return = DWORD;
+#[cfg(unix)] type rust_thread_return = *libc::c_void;
 
-pub struct Thread {
+type StartFn = extern "C" fn(*libc::c_void) -> rust_thread_return;
+
+/// This struct represents a native thread's state. This is used to join on an
+/// existing thread created in the join-able state.
+pub struct Thread<T> {
     priv native: rust_thread,
-    priv joined: bool
+    priv joined: bool,
+    priv packet: ~Option<T>,
 }
 
 static DEFAULT_STACK_SIZE: libc::size_t = 1024*1024;
 
-#[cfg(windows)] type rust_thread_return = DWORD;
-#[cfg(unix)] type rust_thread_return = *libc::c_void;
+// This is the starting point of rust os threads. The first thing we do
+// is make sure that we don't trigger __morestack (also why this has a
+// no_split_stack annotation), and then we extract the main function
+// and invoke it.
+#[no_split_stack]
+extern fn thread_start(main: *libc::c_void) -> rust_thread_return {
+    use rt::context;
+    unsafe {
+        context::record_stack_bounds(0, uint::max_value);
+        let f: ~proc() = cast::transmute(main);
+        (*f)();
+        cast::transmute(0 as rust_thread_return)
+    }
+}
 
-impl Thread {
-
-    pub fn start(main: proc()) -> Thread {
-        // This is the starting point of rust os threads. The first thing we do
-        // is make sure that we don't trigger __morestack (also why this has a
-        // no_split_stack annotation), and then we extract the main function
-        // and invoke it.
-        #[no_split_stack]
-        extern "C" fn thread_start(trampoline: *libc::c_void) -> rust_thread_return {
-            use rt::context;
-            unsafe {
-                context::record_stack_bounds(0, uint::max_value);
-                let f: ~proc() = cast::transmute(trampoline);
-                (*f)();
-            }
-            unsafe { cast::transmute(0 as rust_thread_return) }
-        }
+// There are two impl blocks b/c if T were specified at the top then it's just a
+// pain to specify a type parameter on Thread::spawn (which doesn't need the
+// type parameter).
+impl Thread<()> {
+
+    /// Starts execution of a new OS thread.
+    ///
+    /// This function will not wait for the thread to join, but a handle to the
+    /// thread will be returned.
+    ///
+    /// Note that the handle returned is used to acquire the return value of the
+    /// procedure `main`. The `join` function will wait for the thread to finish
+    /// and return the value that `main` generated.
+    ///
+    /// Also note that the `Thread` returned will *always* wait for the thread
+    /// to finish executing. This means that even if `join` is not explicitly
+    /// called, when the `Thread` falls out of scope its destructor will block
+    /// waiting for the OS thread.
+    pub fn start<T: Send>(main: proc() -> T) -> Thread<T> {
+
+        // We need the address of the packet to fill in to be stable so when
+        // `main` fills it in it's still valid, so allocate an extra ~ box to do
+        // so.
+        let packet = ~None;
+        let packet2: *mut Option<T> = unsafe {
+            *cast::transmute::<&~Option<T>, **mut Option<T>>(&packet)
+        };
+        let main: proc() = proc() unsafe { *packet2 = Some(main()); };
+        let native = unsafe { native_thread_create(~main) };
 
-        let native = native_thread_create(thread_start, ~main);
         Thread {
             native: native,
             joined: false,
+            packet: packet,
         }
     }
 
-    pub fn join(mut self) {
+    /// This will spawn a new thread, but it will not wait for the thread to
+    /// finish, nor is it possible to wait for the thread to finish.
+    ///
+    /// This corresponds to creating threads in the 'detached' state on unix
+    /// systems. Note that platforms may not keep the main program alive even if
+    /// there are detached thread still running around.
+    pub fn spawn(main: proc()) {
+        unsafe {
+            let handle = native_thread_create(~main);
+            native_thread_detach(handle);
+        }
+    }
+}
+
+impl<T: Send> Thread<T> {
+    /// Wait for this thread to finish, returning the result of the thread's
+    /// calculation.
+    pub fn join(mut self) -> T {
         assert!(!self.joined);
-        native_thread_join(self.native);
+        unsafe { native_thread_join(self.native) };
         self.joined = true;
+        assert!(self.packet.is_some());
+        self.packet.take_unwrap()
     }
 }
 
-#[cfg(windows)]
-fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return,
-                        tramp: ~proc()) -> rust_thread {
-    unsafe {
-        let ptr: *mut libc::c_void = cast::transmute(tramp);
-        CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start, ptr, 0, ptr::mut_null())
+#[unsafe_destructor]
+impl<T: Send> Drop for Thread<T> {
+    fn drop(&mut self) {
+        // This is required for correctness. If this is not done then the thread
+        // would fill in a return box which no longer exists.
+        if !self.joined {
+            unsafe { native_thread_join(self.native) };
+        }
     }
 }
 
 #[cfg(windows)]
-fn native_thread_join(native: rust_thread) {
+unsafe fn native_thread_create(p: ~proc()) -> rust_thread {
+    let arg: *mut libc::c_void = cast::transmute(p);
+    CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start,
+                 arg, 0, ptr::mut_null())
+}
+
+#[cfg(windows)]
+unsafe fn native_thread_join(native: rust_thread) {
     use libc::consts::os::extra::INFINITE;
-    unsafe { WaitForSingleObject(native, INFINITE); }
+    WaitForSingleObject(native, INFINITE);
+}
+
+#[cfg(windows)]
+unsafe fn native_thread_detach(native: rust_thread) {
+    assert!(libc::CloseHandle(native) != 0);
 }
 
 #[cfg(unix)]
-fn native_thread_create(thread_start: extern "C" fn(*libc::c_void) -> rust_thread_return,
-                        tramp: ~proc()) -> rust_thread {
+unsafe fn native_thread_create(p: ~proc()) -> rust_thread {
     use unstable::intrinsics;
-    let mut native: libc::pthread_t = unsafe { intrinsics::uninit() };
-
-    unsafe {
-        use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE;
+    use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE;
 
-        let mut attr: libc::pthread_attr_t = intrinsics::uninit();
-        assert!(pthread_attr_init(&mut attr) == 0);
-        assert!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE) == 0);
-        assert!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE) == 0);
+    let mut native: libc::pthread_t = intrinsics::uninit();
+    let mut attr: libc::pthread_attr_t = intrinsics::uninit();
+    assert_eq!(pthread_attr_init(&mut attr), 0);
+    assert_eq!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE), 0);
+    assert_eq!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE), 0);
 
-        let ptr: *libc::c_void = cast::transmute(tramp);
-        assert!(pthread_create(&mut native, &attr, thread_start, ptr) == 0);
-    }
+    let arg: *libc::c_void = cast::transmute(p);
+    assert_eq!(pthread_create(&mut native, &attr, thread_start, arg), 0);
     native
 }
 
 #[cfg(unix)]
-fn native_thread_join(native: rust_thread) {
-    unsafe { assert!(pthread_join(native, ptr::null()) == 0) }
+unsafe fn native_thread_join(native: rust_thread) {
+    assert_eq!(pthread_join(native, ptr::null()), 0);
 }
 
-impl Drop for Thread {
-    fn drop(&mut self) {
-        assert!(self.joined);
-    }
+#[cfg(unix)]
+fn native_thread_detach(native: rust_thread) {
+    unsafe { assert_eq!(pthread_detach(native), 0) }
 }
 
 #[cfg(windows)]
 extern "system" {
-    fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T,
-                    lpStartAddress: extern "C" fn(*libc::c_void) -> rust_thread_return,
-                    lpParameter: LPVOID, dwCreationFlags: DWORD, lpThreadId: LPDWORD) -> HANDLE;
+    fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES,
+                    dwStackSize: SIZE_T,
+                    lpStartAddress: StartFn,
+                    lpParameter: LPVOID,
+                    dwCreationFlags: DWORD,
+                    lpThreadId: LPDWORD) -> HANDLE;
     fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
 }
 
 #[cfg(unix)]
 extern {
-    fn pthread_create(native: *mut libc::pthread_t, attr: *libc::pthread_attr_t,
-                      f: extern "C" fn(*libc::c_void) -> rust_thread_return,
+    fn pthread_create(native: *mut libc::pthread_t,
+                      attr: *libc::pthread_attr_t,
+                      f: StartFn,
                       value: *libc::c_void) -> libc::c_int;
-    fn pthread_join(native: libc::pthread_t, value: **libc::c_void) -> libc::c_int;
+    fn pthread_join(native: libc::pthread_t,
+                    value: **libc::c_void) -> libc::c_int;
     fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
     fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
                                  stack_size: libc::size_t) -> libc::c_int;
     fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
                                    state: libc::c_int) -> libc::c_int;
+    fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Thread;
+
+    #[test]
+    fn smoke() { do Thread::start {}.join(); }
+
+    #[test]
+    fn data() { assert_eq!(do Thread::start { 1 }.join(), 1); }
+
+    #[test]
+    fn detached() { do Thread::spawn {} }
 }
diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs
index 6c1c28c9805..198fe596a89 100644
--- a/src/libstd/task/spawn.rs
+++ b/src/libstd/task/spawn.rs
@@ -139,7 +139,7 @@ pub fn spawn_raw(mut opts: TaskOpts, f: proc()) {
             let join_task = do Task::build_child(None) {
                 debug!("running join task");
                 let thread_port = thread_port_cell.take();
-                let thread: Thread = thread_port.recv();
+                let thread: Thread<()> = thread_port.recv();
                 thread.join();
             };