Skip to content

Commit 5759cff

Browse files
alexcrichtonaturon
authored andcommitted
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.
1 parent a27fbac commit 5759cff

File tree

2 files changed

+78
-46
lines changed

2 files changed

+78
-46
lines changed

src/libstd/rt/at_exit_imp.rs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,60 @@ use core::prelude::*;
1616

1717
use boxed::Box;
1818
use vec::Vec;
19-
use sync::{Mutex, atomic, Once, ONCE_INIT};
2019
use mem;
2120
use thunk::Thunk;
21+
use sys_common::mutex::{Mutex, MUTEX_INIT};
2222

23-
type Queue = Mutex<Vec<Thunk>>;
23+
type Queue = Vec<Thunk>;
2424

25-
static INIT: Once = ONCE_INIT;
26-
static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT;
25+
// NB these are specifically not types from `std::sync` as they currently rely
26+
// on poisoning and this module needs to operate at a lower level than requiring
27+
// the thread infrastructure to be in place (useful on the borders of
28+
// initialization/destruction).
29+
static LOCK: Mutex = MUTEX_INIT;
30+
static mut QUEUE: *mut Queue = 0 as *mut Queue;
2731

28-
fn init() {
29-
let state: Box<Queue> = box Mutex::new(Vec::new());
30-
unsafe {
31-
QUEUE.store(mem::transmute(state), atomic::SeqCst);
32-
33-
// FIXME: switch this to use atexit as below. Currently this
34-
// segfaults (the queue's memory is mysteriously gone), so
35-
// instead the cleanup is tied to the `std::rt` entry point.
36-
//
37-
// ::libc::atexit(cleanup);
32+
unsafe fn init() {
33+
if QUEUE.is_null() {
34+
let state: Box<Queue> = box Vec::new();
35+
QUEUE = mem::transmute(state);
36+
} else {
37+
// can't re-init after a cleanup
38+
rtassert!(QUEUE as uint != 1);
3839
}
40+
41+
// FIXME: switch this to use atexit as below. Currently this
42+
// segfaults (the queue's memory is mysteriously gone), so
43+
// instead the cleanup is tied to the `std::rt` entry point.
44+
//
45+
// ::libc::atexit(cleanup);
3946
}
4047

4148
pub fn cleanup() {
4249
unsafe {
43-
let queue = QUEUE.swap(0, atomic::SeqCst);
44-
if queue != 0 {
50+
LOCK.lock();
51+
let queue = QUEUE;
52+
QUEUE = 1 as *mut _;
53+
LOCK.unlock();
54+
55+
// make sure we're not recursively cleaning up
56+
rtassert!(queue as uint != 1);
57+
58+
// If we never called init, not need to cleanup!
59+
if queue as uint != 0 {
4560
let queue: Box<Queue> = mem::transmute(queue);
46-
let v = mem::replace(&mut *queue.lock(), Vec::new());
47-
for to_run in v.into_iter() {
61+
for to_run in queue.into_iter() {
4862
to_run.invoke(());
4963
}
5064
}
5165
}
5266
}
5367

5468
pub fn push(f: Thunk) {
55-
INIT.doit(init);
5669
unsafe {
57-
// Note that the check against 0 for the queue pointer is not atomic at
58-
// all with respect to `run`, meaning that this could theoretically be a
59-
// use-after-free. There's not much we can do to protect against that,
60-
// however. Let's just assume a well-behaved runtime and go from there!
61-
let queue = QUEUE.load(atomic::SeqCst);
62-
rtassert!(queue != 0);
63-
(*(queue as *const Queue)).lock().push(f);
70+
LOCK.lock();
71+
init();
72+
(*QUEUE).push(f);
73+
LOCK.unlock();
6474
}
6575
}

src/libstd/sys/windows/thread_local.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
1414

1515
use mem;
1616
use rt;
17-
use sync::{ONCE_INIT, Once, Mutex};
17+
use sys_common::mutex::{MUTEX_INIT, Mutex};
1818

1919
pub type Key = DWORD;
2020
pub type Dtor = unsafe extern fn(*mut u8);
@@ -53,8 +53,12 @@ pub type Dtor = unsafe extern fn(*mut u8);
5353
// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base
5454
// /threading/thread_local_storage_win.cc#L42
5555

56-
static INIT_DTORS: Once = ONCE_INIT;
57-
static mut DTORS: *mut Mutex<Vec<(Key, Dtor)>> = 0 as *mut _;
56+
// NB these are specifically not types from `std::sync` as they currently rely
57+
// on poisoning and this module needs to operate at a lower level than requiring
58+
// the thread infrastructure to be in place (useful on the borders of
59+
// initialization/destruction).
60+
static DTOR_LOCK: Mutex = MUTEX_INIT;
61+
static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _;
5862

5963
// -------------------------------------------------------------------------
6064
// Native bindings
@@ -124,30 +128,40 @@ extern "system" {
124128
//
125129
// FIXME: This could probably be at least a little faster with a BTree.
126130

127-
fn init_dtors() {
128-
let dtors = box Mutex::new(Vec::<(Key, Dtor)>::new());
129-
unsafe {
130-
DTORS = mem::transmute(dtors);
131-
}
131+
unsafe fn init_dtors() {
132+
if !DTORS.is_null() { return }
133+
134+
let dtors = box Vec::<(Key, Dtor)>::new();
135+
DTORS = mem::transmute(dtors);
132136

133-
rt::at_exit(move|| unsafe {
134-
mem::transmute::<_, Box<Mutex<Vec<(Key, Dtor)>>>>(DTORS);
137+
rt::at_exit(move|| {
138+
DTOR_LOCK.lock();
139+
let dtors = DTORS;
135140
DTORS = 0 as *mut _;
141+
mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
142+
assert!(DTORS.is_null()); // can't re-init after destructing
143+
DTOR_LOCK.unlock();
136144
});
137145
}
138146

139147
unsafe fn register_dtor(key: Key, dtor: Dtor) {
140-
INIT_DTORS.doit(init_dtors);
141-
let mut dtors = (*DTORS).lock();
142-
dtors.push((key, dtor));
148+
DTOR_LOCK.lock();
149+
init_dtors();
150+
(*DTORS).push((key, dtor));
151+
DTOR_LOCK.unlock();
143152
}
144153

145154
unsafe fn unregister_dtor(key: Key) -> bool {
146-
if DTORS.is_null() { return false }
147-
let mut dtors = (*DTORS).lock();
148-
let before = dtors.len();
149-
dtors.retain(|&(k, _)| k != key);
150-
dtors.len() != before
155+
DTOR_LOCK.lock();
156+
init_dtors();
157+
let ret = {
158+
let dtors = &mut *DTORS;
159+
let before = dtors.len();
160+
dtors.retain(|&(k, _)| k != key);
161+
dtors.len() != before
162+
};
163+
DTOR_LOCK.unlock();
164+
ret
151165
}
152166

153167
// -------------------------------------------------------------------------
@@ -219,12 +233,20 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID,
219233
}
220234

221235
unsafe fn run_dtors() {
222-
if DTORS.is_null() { return }
223236
let mut any_run = true;
224237
for _ in range(0, 5i) {
225238
if !any_run { break }
226239
any_run = false;
227-
let dtors = (*DTORS).lock().iter().map(|p| *p).collect::<Vec<_>>();
240+
let dtors = {
241+
DTOR_LOCK.lock();
242+
let ret = if DTORS.is_null() {
243+
Vec::new()
244+
} else {
245+
(*DTORS).iter().map(|s| *s).collect()
246+
};
247+
DTOR_LOCK.unlock();
248+
ret
249+
};
228250
for &(key, dtor) in dtors.iter() {
229251
let ptr = TlsGetValue(key);
230252
if !ptr.is_null() {

0 commit comments

Comments
 (0)