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(());
}
}
}
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();
}
}
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);
// [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
//
// 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
}
// -------------------------------------------------------------------------
}
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() {