diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-12-17 14:37:38 -0800 |
|---|---|---|
| committer | Aaron Turon <aturon@mozilla.com> | 2014-12-18 23:35:52 -0800 |
| commit | 5759cff48e66bcf2bf2cf821211bdf683292d8f3 (patch) | |
| tree | 4532178a8794666d1d62300b42981a9ccdf3a49d /src/libstd | |
| parent | a27fbac86849e07a0a6c746869d8f78319bd3a16 (diff) | |
| download | rust-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.rs | 62 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread_local.rs | 62 |
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() { |
