about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-12-17 14:37:38 -0800
committerAaron Turon <aturon@mozilla.com>2014-12-18 23:35:52 -0800
commit5759cff48e66bcf2bf2cf821211bdf683292d8f3 (patch)
tree4532178a8794666d1d62300b42981a9ccdf3a49d /src/libstd
parenta27fbac86849e07a0a6c746869d8f78319bd3a16 (diff)
downloadrust-5759cff48e66bcf2bf2cf821211bdf683292d8f3.tar.gz
rust-5759cff48e66bcf2bf2cf821211bdf683292d8f3.zip
std: Lower abstractions for thread_local/at_exit
The current implementations use `std::sync` primitives, but these primitives
currently end up relying on `thread_info` and a local `Thread` being available
(mainly for checking the panicking flag).

To get around this, this commit lowers the abstractions used by the windows
thread_local implementation as well as the at_exit_imp module. Both of these
modules now use a `sys::Mutex` and a `static mut` and manage the
allocation/locking manually.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rt/at_exit_imp.rs62
-rw-r--r--src/libstd/sys/windows/thread_local.rs62
2 files changed, 78 insertions, 46 deletions
diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs
index b63b4ced005..5823f8453d8 100644
--- a/src/libstd/rt/at_exit_imp.rs
+++ b/src/libstd/rt/at_exit_imp.rs
@@ -16,35 +16,49 @@ use core::prelude::*;
 
 use boxed::Box;
 use vec::Vec;
-use sync::{Mutex, atomic, Once, ONCE_INIT};
 use mem;
 use thunk::Thunk;
+use sys_common::mutex::{Mutex, MUTEX_INIT};
 
-type Queue = Mutex<Vec<Thunk>>;
+type Queue = Vec<Thunk>;
 
-static INIT: Once = ONCE_INIT;
-static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT;
+// NB these are specifically not types from `std::sync` as they currently rely
+// on poisoning and this module needs to operate at a lower level than requiring
+// the thread infrastructure to be in place (useful on the borders of
+// initialization/destruction).
+static LOCK: Mutex = MUTEX_INIT;
+static mut QUEUE: *mut Queue = 0 as *mut Queue;
 
-fn init() {
-    let state: Box<Queue> = box Mutex::new(Vec::new());
-    unsafe {
-        QUEUE.store(mem::transmute(state), atomic::SeqCst);
-
-        // FIXME: switch this to use atexit as below. Currently this
-        // segfaults (the queue's memory is mysteriously gone), so
-        // instead the cleanup is tied to the `std::rt` entry point.
-        //
-        // ::libc::atexit(cleanup);
+unsafe fn init() {
+    if QUEUE.is_null() {
+        let state: Box<Queue> = box Vec::new();
+        QUEUE = mem::transmute(state);
+    } else {
+        // can't re-init after a cleanup
+        rtassert!(QUEUE as uint != 1);
     }
+
+    // FIXME: switch this to use atexit as below. Currently this
+    // segfaults (the queue's memory is mysteriously gone), so
+    // instead the cleanup is tied to the `std::rt` entry point.
+    //
+    // ::libc::atexit(cleanup);
 }
 
 pub fn cleanup() {
     unsafe {
-        let queue = QUEUE.swap(0, atomic::SeqCst);
-        if queue != 0 {
+        LOCK.lock();
+        let queue = QUEUE;
+        QUEUE = 1 as *mut _;
+        LOCK.unlock();
+
+        // make sure we're not recursively cleaning up
+        rtassert!(queue as uint != 1);
+
+        // If we never called init, not need to cleanup!
+        if queue as uint != 0 {
             let queue: Box<Queue> = mem::transmute(queue);
-            let v = mem::replace(&mut *queue.lock(), Vec::new());
-            for to_run in v.into_iter() {
+            for to_run in queue.into_iter() {
                 to_run.invoke(());
             }
         }
@@ -52,14 +66,10 @@ pub fn cleanup() {
 }
 
 pub fn push(f: Thunk) {
-    INIT.doit(init);
     unsafe {
-        // Note that the check against 0 for the queue pointer is not atomic at
-        // all with respect to `run`, meaning that this could theoretically be a
-        // use-after-free. There's not much we can do to protect against that,
-        // however. Let's just assume a well-behaved runtime and go from there!
-        let queue = QUEUE.load(atomic::SeqCst);
-        rtassert!(queue != 0);
-        (*(queue as *const Queue)).lock().push(f);
+        LOCK.lock();
+        init();
+        (*QUEUE).push(f);
+        LOCK.unlock();
     }
 }
diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs
index 0644519aabb..60b0d584db3 100644
--- a/src/libstd/sys/windows/thread_local.rs
+++ b/src/libstd/sys/windows/thread_local.rs
@@ -14,7 +14,7 @@ use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
 
 use mem;
 use rt;
-use sync::{ONCE_INIT, Once, Mutex};
+use sys_common::mutex::{MUTEX_INIT, Mutex};
 
 pub type Key = DWORD;
 pub type Dtor = unsafe extern fn(*mut u8);
@@ -53,8 +53,12 @@ pub type Dtor = unsafe extern fn(*mut u8);
 // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
 //                        /threading/thread_local_storage_win.cc#L42
 
-static INIT_DTORS: Once = ONCE_INIT;
-static mut DTORS: *mut Mutex<Vec<(Key, Dtor)>> = 0 as *mut _;
+// NB these are specifically not types from `std::sync` as they currently rely
+// on poisoning and this module needs to operate at a lower level than requiring
+// the thread infrastructure to be in place (useful on the borders of
+// initialization/destruction).
+static DTOR_LOCK: Mutex = MUTEX_INIT;
+static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _;
 
 // -------------------------------------------------------------------------
 // Native bindings
@@ -124,30 +128,40 @@ extern "system" {
 //
 // FIXME: This could probably be at least a little faster with a BTree.
 
-fn init_dtors() {
-    let dtors = box Mutex::new(Vec::<(Key, Dtor)>::new());
-    unsafe {
-        DTORS = mem::transmute(dtors);
-    }
+unsafe fn init_dtors() {
+    if !DTORS.is_null() { return }
+
+    let dtors = box Vec::<(Key, Dtor)>::new();
+    DTORS = mem::transmute(dtors);
 
-    rt::at_exit(move|| unsafe {
-        mem::transmute::<_, Box<Mutex<Vec<(Key, Dtor)>>>>(DTORS);
+    rt::at_exit(move|| {
+        DTOR_LOCK.lock();
+        let dtors = DTORS;
         DTORS = 0 as *mut _;
+        mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
+        assert!(DTORS.is_null()); // can't re-init after destructing
+        DTOR_LOCK.unlock();
     });
 }
 
 unsafe fn register_dtor(key: Key, dtor: Dtor) {
-    INIT_DTORS.doit(init_dtors);
-    let mut dtors = (*DTORS).lock();
-    dtors.push((key, dtor));
+    DTOR_LOCK.lock();
+    init_dtors();
+    (*DTORS).push((key, dtor));
+    DTOR_LOCK.unlock();
 }
 
 unsafe fn unregister_dtor(key: Key) -> bool {
-    if DTORS.is_null() { return false }
-    let mut dtors = (*DTORS).lock();
-    let before = dtors.len();
-    dtors.retain(|&(k, _)| k != key);
-    dtors.len() != before
+    DTOR_LOCK.lock();
+    init_dtors();
+    let ret = {
+        let dtors = &mut *DTORS;
+        let before = dtors.len();
+        dtors.retain(|&(k, _)| k != key);
+        dtors.len() != before
+    };
+    DTOR_LOCK.unlock();
+    ret
 }
 
 // -------------------------------------------------------------------------
@@ -219,12 +233,20 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID,
 }
 
 unsafe fn run_dtors() {
-    if DTORS.is_null() { return }
     let mut any_run = true;
     for _ in range(0, 5i) {
         if !any_run { break }
         any_run = false;
-        let dtors = (*DTORS).lock().iter().map(|p| *p).collect::<Vec<_>>();
+        let dtors = {
+            DTOR_LOCK.lock();
+            let ret = if DTORS.is_null() {
+                Vec::new()
+            } else {
+                (*DTORS).iter().map(|s| *s).collect()
+            };
+            DTOR_LOCK.unlock();
+            ret
+        };
         for &(key, dtor) in dtors.iter() {
             let ptr = TlsGetValue(key);
             if !ptr.is_null() {