Skip to content

Commit af160bd

Browse files
committed
Use System allocator for thread-local storage
1 parent 5c7ae0c commit af160bd

File tree

5 files changed

+98
-27
lines changed

5 files changed

+98
-27
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: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::key::{Key, LazyKey, get, set};
22
use super::{abort_on_dtor_unwind, guard};
3-
use crate::alloc::{self, Layout};
3+
use crate::alloc::{self, Layout, System};
44
use crate::cell::Cell;
55
use crate::marker::PhantomData;
66
use crate::mem::ManuallyDrop;
@@ -113,17 +113,19 @@ pub const fn value_align<T: 'static>() -> usize {
113113
crate::mem::align_of::<Value<T>>()
114114
}
115115

116-
/// Equivalent to `Box<Value<T>>`, but potentially over-aligned.
117-
struct AlignedBox<T: 'static, const ALIGN: usize> {
116+
/// Equivalent to `Box<Value<T>, System>`, but potentially over-aligned.
117+
struct AlignedSystemBox<T: 'static, const ALIGN: usize> {
118118
ptr: NonNull<Value<T>>,
119119
}
120120

121-
impl<T: 'static, const ALIGN: usize> AlignedBox<T, ALIGN> {
121+
impl<T: 'static, const ALIGN: usize> AlignedSystemBox<T, ALIGN> {
122122
#[inline]
123123
fn new(v: Value<T>) -> Self {
124124
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
125125

126-
let ptr: *mut Value<T> = (unsafe { alloc::alloc(layout) }).cast();
126+
// We use the System allocator here to avoid interfering with a potential
127+
// Global allocator using thread-local storage.
128+
let ptr: *mut Value<T> = (unsafe { System.alloc(layout) }).cast();
127129
let Some(ptr) = NonNull::new(ptr) else {
128130
alloc::handle_alloc_error(layout);
129131
};
@@ -143,7 +145,7 @@ impl<T: 'static, const ALIGN: usize> AlignedBox<T, ALIGN> {
143145
}
144146
}
145147

146-
impl<T: 'static, const ALIGN: usize> Deref for AlignedBox<T, ALIGN> {
148+
impl<T: 'static, const ALIGN: usize> Deref for AlignedSystemBox<T, ALIGN> {
147149
type Target = Value<T>;
148150

149151
#[inline]
@@ -152,14 +154,14 @@ impl<T: 'static, const ALIGN: usize> Deref for AlignedBox<T, ALIGN> {
152154
}
153155
}
154156

155-
impl<T: 'static, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
157+
impl<T: 'static, const ALIGN: usize> Drop for AlignedSystemBox<T, ALIGN> {
156158
#[inline]
157159
fn drop(&mut self) {
158160
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
159161

160162
unsafe {
161163
let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place()));
162-
alloc::dealloc(self.ptr.as_ptr().cast(), layout);
164+
System.dealloc(self.ptr.as_ptr().cast(), layout);
163165
if let Err(payload) = unwind_result {
164166
resume_unwind(payload);
165167
}
@@ -205,11 +207,11 @@ impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
205207
return ptr::null();
206208
}
207209

208-
let value = AlignedBox::<T, ALIGN>::new(Value {
210+
let value = AlignedSystemBox::<T, ALIGN>::new(Value {
209211
value: i.and_then(Option::take).unwrap_or_else(f),
210212
key,
211213
});
212-
let ptr = AlignedBox::into_raw(value);
214+
let ptr = AlignedSystemBox::into_raw(value);
213215

214216
// SAFETY:
215217
// * key came from a `LazyKey` and is thus correct.
@@ -227,7 +229,7 @@ impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
227229
// initializer has already returned and the next scope only starts
228230
// after we return the pointer. Therefore, there can be no references
229231
// to the old value.
230-
drop(unsafe { AlignedBox::<T, ALIGN>::from_raw(old) });
232+
drop(unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(old) });
231233
}
232234

233235
// SAFETY: We just created this value above.
@@ -246,7 +248,7 @@ unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8)
246248
// Note that to prevent an infinite loop we reset it back to null right
247249
// before we return from the destructor ourselves.
248250
abort_on_dtor_unwind(|| {
249-
let ptr = unsafe { AlignedBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
251+
let ptr = unsafe { AlignedSystemBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
250252
let key = ptr.key;
251253
// SAFETY: `key` is the TLS key `ptr` was stored under.
252254
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;
@@ -1466,7 +1467,10 @@ impl Inner {
14661467
///
14671468
/// [`thread::current`]: current::current
14681469
pub struct Thread {
1469-
inner: Pin<Arc<Inner>>,
1470+
// We use the System allocator such that creating or dropping this handle
1471+
// does not interfere with a potential Global allocator using thread-local
1472+
// storage.
1473+
inner: Pin<Arc<Inner, System>>,
14701474
}
14711475

14721476
impl Thread {
@@ -1479,7 +1483,7 @@ impl Thread {
14791483
// SAFETY: We pin the Arc immediately after creation, so its address never
14801484
// changes.
14811485
let inner = unsafe {
1482-
let mut arc = Arc::<Inner>::new_uninit();
1486+
let mut arc = Arc::<Inner, _>::new_uninit_in(System);
14831487
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
14841488
(&raw mut (*ptr).name).write(name);
14851489
(&raw mut (*ptr).id).write(id);
@@ -1650,7 +1654,7 @@ impl Thread {
16501654
pub fn into_raw(self) -> *const () {
16511655
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
16521656
let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
1653-
Arc::into_raw(inner) as *const ()
1657+
Arc::into_raw_with_allocator(inner).0 as *const ()
16541658
}
16551659

16561660
/// Constructs a `Thread` from a raw pointer.
@@ -1672,7 +1676,9 @@ impl Thread {
16721676
#[unstable(feature = "thread_raw", issue = "97523")]
16731677
pub unsafe fn from_raw(ptr: *const ()) -> Thread {
16741678
// Safety: Upheld by caller.
1675-
unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } }
1679+
unsafe {
1680+
Thread { inner: Pin::new_unchecked(Arc::from_raw_in(ptr as *const Inner, System)) }
1681+
}
16761682
}
16771683

16781684
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)