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.
This commit is contained in:
parent
a27fbac868
commit
5759cff48e
src/libstd
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
|
||||
rt::at_exit(move|| unsafe {
|
||||
mem::transmute::<_, Box<Mutex<Vec<(Key, Dtor)>>>>(DTORS);
|
||||
let dtors = box Vec::<(Key, Dtor)>::new();
|
||||
DTORS = mem::transmute(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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user