Skip to content

Commit d7692cc

Browse files
committed
Use System allocator for thread-local storage
1 parent 8df154b commit d7692cc

File tree

5 files changed

+92
-19
lines changed

5 files changed

+92
-19
lines changed

library/std/src/sys/thread_local/destructors/list.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
1+
use crate::alloc::System;
12
use crate::cell::RefCell;
23
use crate::sys::thread_local::guard;
34

45
#[thread_local]
5-
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
6+
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8)), System>> =
7+
RefCell::new(Vec::new_in(System));
68

79
pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
8-
let Ok(mut dtors) = DTORS.try_borrow_mut() else {
9-
// This point can only be reached if the global allocator calls this
10-
// function again.
11-
// FIXME: maybe use the system allocator instead?
12-
rtabort!("the global allocator may not use TLS with destructors");
13-
};
14-
10+
let Ok(mut dtors) = DTORS.try_borrow_mut() else { rtabort!("unreachable") };
1511
guard::enable();
16-
1712
dtors.push((t, dtor));
1813
}
1914

@@ -36,7 +31,7 @@ pub unsafe fn run() {
3631
}
3732
None => {
3833
// Free the list memory.
39-
*dtors = Vec::new();
34+
*dtors = Vec::new_in(System);
4035
break;
4136
}
4237
}

library/std/src/sys/thread_local/key/xous.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
use core::arch::asm;
4040

41+
use crate::alloc::System;
4142
use crate::mem::ManuallyDrop;
4243
use crate::os::xous::ffi::{MemoryFlags, map_memory, unmap_memory};
4344
use crate::ptr;
@@ -151,7 +152,10 @@ struct Node {
151152
}
152153

153154
unsafe fn register_dtor(key: Key, dtor: Dtor) {
154-
let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() }));
155+
// We use the System allocator here to avoid interfering with a potential
156+
// Global allocator using thread-local storage.
157+
let mut node =
158+
ManuallyDrop::new(Box::new_in(Node { key, dtor, next: ptr::null_mut() }, System));
155159

156160
#[allow(unused_unsafe)]
157161
let mut head = unsafe { DTORS.load(Acquire) };

library/std/src/sys/thread_local/os.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::key::{Key, LazyKey, get, set};
22
use super::{abort_on_dtor_unwind, guard};
3+
use crate::alloc::System;
34
use crate::cell::Cell;
45
use crate::marker::PhantomData;
56
use crate::ptr;
@@ -95,8 +96,11 @@ impl<T: 'static> Storage<T> {
9596
return ptr::null();
9697
}
9798

98-
let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key });
99-
let ptr = Box::into_raw(value);
99+
// We use the System allocator here to avoid interfering with a potential
100+
// Global allocator using thread-local storage.
101+
let value =
102+
Box::new_in(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }, System);
103+
let ptr = Box::into_raw_with_allocator(value).0;
100104

101105
// SAFETY:
102106
// * key came from a `LazyKey` and is thus correct.
@@ -114,7 +118,7 @@ impl<T: 'static> Storage<T> {
114118
// initializer has already returned and the next scope only starts
115119
// after we return the pointer. Therefore, there can be no references
116120
// to the old value.
117-
drop(unsafe { Box::from_raw(old) });
121+
drop(unsafe { Box::from_raw_in(old, System) });
118122
}
119123

120124
// SAFETY: We just created this value above.
@@ -133,7 +137,7 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
133137
// Note that to prevent an infinite loop we reset it back to null right
134138
// before we return from the destructor ourselves.
135139
abort_on_dtor_unwind(|| {
136-
let ptr = unsafe { Box::from_raw(ptr as *mut Value<T>) };
140+
let ptr = unsafe { Box::from_raw_in(ptr as *mut Value<T>, System) };
137141
let key = ptr.key;
138142
// SAFETY: `key` is the TLS key `ptr` was stored under.
139143
unsafe { set(key, ptr::without_provenance_mut(1)) };

library/std/src/thread/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@
158158
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
159159
mod tests;
160160

161+
use crate::alloc::System;
161162
use crate::any::Any;
162163
use crate::cell::UnsafeCell;
163164
use crate::ffi::CStr;
@@ -1439,7 +1440,10 @@ impl Inner {
14391440
///
14401441
/// [`thread::current`]: current::current
14411442
pub struct Thread {
1442-
inner: Pin<Arc<Inner>>,
1443+
// We use the System allocator such that creating or dropping this handle
1444+
// does not interfere with a potential Global allocator using thread-local
1445+
// storage.
1446+
inner: Pin<Arc<Inner, System>>,
14431447
}
14441448

14451449
impl Thread {
@@ -1452,7 +1456,7 @@ impl Thread {
14521456
// SAFETY: We pin the Arc immediately after creation, so its address never
14531457
// changes.
14541458
let inner = unsafe {
1455-
let mut arc = Arc::<Inner>::new_uninit();
1459+
let mut arc = Arc::<Inner, _>::new_uninit_in(System);
14561460
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
14571461
(&raw mut (*ptr).name).write(name);
14581462
(&raw mut (*ptr).id).write(id);
@@ -1610,7 +1614,7 @@ impl Thread {
16101614
pub fn into_raw(self) -> *const () {
16111615
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
16121616
let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
1613-
Arc::into_raw(inner) as *const ()
1617+
Arc::into_raw_with_allocator(inner).0 as *const ()
16141618
}
16151619

16161620
/// Constructs a `Thread` from a raw pointer.
@@ -1632,7 +1636,9 @@ impl Thread {
16321636
#[unstable(feature = "thread_raw", issue = "97523")]
16331637
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
16341638
// Safety: Upheld by caller.
1635-
unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } }
1639+
unsafe {
1640+
Thread { inner: Pin::new_unchecked(Arc::from_raw_in(ptr as *const Inner, System)) }
1641+
}
16361642
}
16371643

16381644
fn cname(&self) -> Option<&CStr> {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//@ run-pass
2+
//@ needs-threads
3+
4+
use std::alloc::{GlobalAlloc, Layout, System};
5+
use std::thread::Thread;
6+
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
7+
8+
static GLOBAL: AtomicUsize = AtomicUsize::new(0);
9+
10+
struct Local(Thread);
11+
12+
thread_local! {
13+
static LOCAL: Local = {
14+
GLOBAL.fetch_or(1, Ordering::Relaxed);
15+
Local(std::thread::current())
16+
};
17+
}
18+
19+
impl Drop for Local {
20+
fn drop(&mut self) {
21+
GLOBAL.fetch_or(2, Ordering::Relaxed);
22+
}
23+
}
24+
25+
static SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS: AtomicBool = AtomicBool::new(false);
26+
27+
#[global_allocator]
28+
static ALLOC: Alloc = Alloc;
29+
struct Alloc;
30+
31+
unsafe impl GlobalAlloc for Alloc {
32+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
33+
// Make sure we aren't re-entrant.
34+
assert!(!SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.load(Ordering::Relaxed));
35+
SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.store(true, Ordering::Relaxed);
36+
LOCAL.with(|local| {
37+
assert!(local.0.id() == std::thread::current().id());
38+
});
39+
let ret = unsafe { System.alloc(layout) };
40+
SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.store(false, Ordering::Relaxed);
41+
ret
42+
}
43+
44+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
45+
// Make sure we aren't re-entrant.
46+
assert!(!SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.load(Ordering::Relaxed));
47+
SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.store(true, Ordering::Relaxed);
48+
LOCAL.with(|local| {
49+
assert!(local.0.id() == std::thread::current().id());
50+
});
51+
unsafe { System.dealloc(ptr, layout) }
52+
SHOULD_PANIC_ON_GLOBAL_ALLOC_ACCESS.store(false, Ordering::Relaxed);
53+
}
54+
}
55+
56+
fn main() {
57+
std::thread::spawn(|| {
58+
std::hint::black_box(vec![1, 2]);
59+
assert!(GLOBAL.load(Ordering::Relaxed) == 1);
60+
})
61+
.join()
62+
.unwrap();
63+
assert!(GLOBAL.load(Ordering::Relaxed) == 3);
64+
}

0 commit comments

Comments
 (0)