Skip to content

Commit a2d8495

Browse files
committed
Auto merge of #144465 - orlp:system-alloc-tls, r=joboet
Allow the global allocator to use thread-local storage and std::thread::current() Fixes #115209. Currently the thread-local storage implementation uses the `Global` allocator if it needs to allocate memory in some places. This effectively means the global allocator can not use thread-local variables. This is a shame as an allocator is precisely one of the locations where you'd *really* want to use thread-locals. We also see that this lead to hacks such as #116402, where we detect re-entrance and abort. So I've made the places where I could find allocation happening in the TLS implementation use the `System` allocator instead. I also applied this change to the storage allocated for a `Thread` handle so that it may be used care-free in the global allocator as well, for e.g. registering it to a central place or parking primitives. r? `@joboet`
2 parents cf8346d + 8e07390 commit a2d8495

File tree

20 files changed

+384
-210
lines changed

20 files changed

+384
-210
lines changed

library/core/src/alloc/global.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,31 @@ use crate::{cmp, ptr};
115115
/// Whether allocations happen or not is not part of the program behavior, even if it
116116
/// could be detected via an allocator that tracks allocations by printing or otherwise
117117
/// having side effects.
118+
///
119+
/// # Re-entrance
120+
///
121+
/// When implementing a global allocator one has to be careful not to create an infinitely recursive
122+
/// implementation by accident, as many constructs in the Rust standard library may allocate in
123+
/// their implementation. For example, on some platforms [`std::sync::Mutex`] may allocate, so using
124+
/// it is highly problematic in a global allocator.
125+
///
126+
/// Generally speaking for this reason one should stick to library features available through
127+
/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are
128+
/// guaranteed to not use `#[global_allocator]` to allocate:
129+
///
130+
/// - [`std::thread_local`],
131+
/// - [`std::thread::current`],
132+
/// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and
133+
/// [`Clone`] implementation.
134+
///
135+
/// [`std`]: ../../std/index.html
136+
/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html
137+
/// [`std::thread_local`]: ../../std/macro.thread_local.html
138+
/// [`std::thread::current`]: ../../std/thread/fn.current.html
139+
/// [`std::thread::park`]: ../../std/thread/fn.park.html
140+
/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html
141+
/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark
142+
118143
#[stable(feature = "global_alloc", since = "1.28.0")]
119144
pub unsafe trait GlobalAlloc {
120145
/// Allocates memory as described by the given `layout`.

library/std/src/alloc.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//!
1212
//! This attribute allows configuring the choice of global allocator.
1313
//! You can use this to implement a completely custom global allocator
14-
//! to route all default allocation requests to a custom object.
14+
//! to route all[^system-alloc] default allocation requests to a custom object.
1515
//!
1616
//! ```rust
1717
//! use std::alloc::{GlobalAlloc, System, Layout};
@@ -52,6 +52,13 @@
5252
//!
5353
//! The `#[global_allocator]` can only be used once in a crate
5454
//! or its recursive dependencies.
55+
//!
56+
//! [^system-alloc]: Note that the Rust standard library internals may still
57+
//! directly call [`System`] when necessary (for example for the runtime
58+
//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`]
59+
//! for more details).
60+
//!
61+
//! [re-entrance]: trait.GlobalAlloc.html#re-entrance
5562
5663
#![deny(unsafe_op_in_unsafe_fn)]
5764
#![stable(feature = "alloc_module", since = "1.28.0")]

library/std/src/sys/pal/sgx/abi/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use core::arch::global_asm;
44
use core::sync::atomic::{Atomic, AtomicUsize, Ordering};
55

6+
use crate::alloc::System;
67
use crate::io::Write;
78

89
// runtime features
@@ -63,7 +64,9 @@ unsafe extern "C" fn tcs_init(secondary: bool) {
6364
#[unsafe(no_mangle)]
6465
extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn {
6566
// FIXME: how to support TLS in library mode?
66-
let tls = Box::new(tls::Tls::new());
67+
// We use the System allocator here such that the global allocator may use
68+
// thread-locals.
69+
let tls = Box::new_in(tls::Tls::new(), System);
6770
let tls_guard = unsafe { tls.activate() };
6871

6972
if secondary {

library/std/src/sys/pal/sgx/abi/tls/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,6 @@ impl Tls {
8989
ActiveTls { tls: self }
9090
}
9191

92-
#[allow(unused)]
93-
pub unsafe fn activate_persistent(self: Box<Self>) {
94-
// FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
95-
let ptr = Box::into_raw(self).cast_const().cast::<u8>();
96-
unsafe { set_tls_ptr(ptr) };
97-
}
98-
9992
unsafe fn current<'a>() -> &'a Tls {
10093
// FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition.
10194
unsafe { &*(get_tls_ptr() as *const Tls) }

library/std/src/sys/thread/hermit.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::num::NonZero;
2+
use crate::thread::ThreadInit;
23
use crate::time::Duration;
34
use crate::{io, ptr};
45

@@ -16,50 +17,49 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20;
1617
impl Thread {
1718
pub unsafe fn new_with_coreid(
1819
stack: usize,
19-
p: Box<dyn FnOnce()>,
20+
init: Box<ThreadInit>,
2021
core_id: isize,
2122
) -> io::Result<Thread> {
22-
let p = Box::into_raw(Box::new(p));
23+
let data = Box::into_raw(init);
2324
let tid = unsafe {
2425
hermit_abi::spawn2(
2526
thread_start,
26-
p.expose_provenance(),
27+
data.expose_provenance(),
2728
hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO),
2829
stack,
2930
core_id,
3031
)
3132
};
3233

3334
return if tid == 0 {
34-
// The thread failed to start and as a result p was not consumed. Therefore, it is
35+
// The thread failed to start and as a result data was not consumed. Therefore, it is
3536
// safe to reconstruct the box so that it gets deallocated.
3637
unsafe {
37-
drop(Box::from_raw(p));
38+
drop(Box::from_raw(data));
3839
}
3940
Err(io::const_error!(io::ErrorKind::Uncategorized, "unable to create thread!"))
4041
} else {
4142
Ok(Thread { tid })
4243
};
4344

44-
extern "C" fn thread_start(main: usize) {
45-
unsafe {
46-
// Finally, let's run some code.
47-
Box::from_raw(ptr::with_exposed_provenance::<Box<dyn FnOnce()>>(main).cast_mut())();
45+
extern "C" fn thread_start(data: usize) {
46+
// SAFETY: we are simply recreating the box that was leaked earlier.
47+
let init =
48+
unsafe { Box::from_raw(ptr::with_exposed_provenance_mut::<ThreadInit>(data)) };
49+
let rust_start = init.init();
50+
rust_start();
4851

49-
// run all destructors
52+
// Run all destructors.
53+
unsafe {
5054
crate::sys::thread_local::destructors::run();
51-
crate::rt::thread_cleanup();
5255
}
56+
crate::rt::thread_cleanup();
5357
}
5458
}
5559

56-
pub unsafe fn new(
57-
stack: usize,
58-
_name: Option<&str>,
59-
p: Box<dyn FnOnce()>,
60-
) -> io::Result<Thread> {
60+
pub unsafe fn new(stack: usize, init: Box<ThreadInit>) -> io::Result<Thread> {
6161
unsafe {
62-
Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
62+
Thread::new_with_coreid(stack, init, -1 /* = no specific core */)
6363
}
6464
}
6565

library/std/src/sys/thread/sgx.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use crate::io;
44
use crate::sys::pal::abi::{thread, usercalls};
5+
use crate::thread::ThreadInit;
56
use crate::time::Duration;
67

78
pub struct Thread(task_queue::JoinHandle);
@@ -13,6 +14,7 @@ pub use self::task_queue::JoinNotifier;
1314
mod task_queue {
1415
use super::wait_notify;
1516
use crate::sync::{Mutex, MutexGuard};
17+
use crate::thread::ThreadInit;
1618

1719
pub type JoinHandle = wait_notify::Waiter;
1820

@@ -25,19 +27,20 @@ mod task_queue {
2527
}
2628

2729
pub(super) struct Task {
28-
p: Box<dyn FnOnce() + Send>,
30+
init: Box<ThreadInit>,
2931
done: JoinNotifier,
3032
}
3133

3234
impl Task {
33-
pub(super) fn new(p: Box<dyn FnOnce() + Send>) -> (Task, JoinHandle) {
35+
pub(super) fn new(init: Box<ThreadInit>) -> (Task, JoinHandle) {
3436
let (done, recv) = wait_notify::new();
3537
let done = JoinNotifier(Some(done));
36-
(Task { p, done }, recv)
38+
(Task { init, done }, recv)
3739
}
3840

3941
pub(super) fn run(self) -> JoinNotifier {
40-
(self.p)();
42+
let rust_start = self.init.init();
43+
rust_start();
4144
self.done
4245
}
4346
}
@@ -93,14 +96,10 @@ pub mod wait_notify {
9396

9497
impl Thread {
9598
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
96-
pub unsafe fn new(
97-
_stack: usize,
98-
_name: Option<&str>,
99-
p: Box<dyn FnOnce() + Send>,
100-
) -> io::Result<Thread> {
99+
pub unsafe fn new(_stack: usize, init: Box<ThreadInit>) -> io::Result<Thread> {
101100
let mut queue_lock = task_queue::lock();
102101
unsafe { usercalls::launch_thread()? };
103-
let (task, handle) = task_queue::Task::new(p);
102+
let (task, handle) = task_queue::Task::new(init);
104103
queue_lock.push(task);
105104
Ok(Thread(handle))
106105
}

library/std/src/sys/thread/solid.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
88
use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting};
99
use crate::sys::pal::itron::time::dur2reltims;
1010
use crate::sys::pal::itron::{abi, task};
11+
use crate::thread::ThreadInit;
1112
use crate::time::Duration;
1213
use crate::{hint, io};
1314

@@ -27,9 +28,9 @@ unsafe impl Sync for Thread {}
2728
/// State data shared between a parent thread and child thread. It's dropped on
2829
/// a transition to one of the final states.
2930
struct ThreadInner {
30-
/// This field is used on thread creation to pass a closure from
31+
/// This field is used on thread creation to pass initialization data from
3132
/// `Thread::new` to the created task.
32-
start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,
33+
init: UnsafeCell<ManuallyDrop<Box<ThreadInit>>>,
3334

3435
/// A state machine. Each transition is annotated with `[...]` in the
3536
/// source code.
@@ -65,7 +66,7 @@ struct ThreadInner {
6566
lifecycle: Atomic<usize>,
6667
}
6768

68-
// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
69+
// Safety: The only `!Sync` field, `ThreadInner::init`, is only touched by
6970
// the task represented by `ThreadInner`.
7071
unsafe impl Sync for ThreadInner {}
7172

@@ -84,13 +85,9 @@ impl Thread {
8485
/// # Safety
8586
///
8687
/// See `thread::Builder::spawn_unchecked` for safety requirements.
87-
pub unsafe fn new(
88-
stack: usize,
89-
_name: Option<&str>,
90-
p: Box<dyn FnOnce()>,
91-
) -> io::Result<Thread> {
88+
pub unsafe fn new(stack: usize, init: Box<ThreadInit>) -> io::Result<Thread> {
9289
let inner = Box::new(ThreadInner {
93-
start: UnsafeCell::new(ManuallyDrop::new(p)),
90+
init: UnsafeCell::new(ManuallyDrop::new(init)),
9491
lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
9592
});
9693

@@ -100,10 +97,11 @@ impl Thread {
10097
let inner = unsafe { &*p_inner };
10198

10299
// Safety: Since `trampoline` is called only once for each
103-
// `ThreadInner` and only `trampoline` touches `start`,
104-
// `start` contains contents and is safe to mutably borrow.
105-
let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
106-
p();
100+
// `ThreadInner` and only `trampoline` touches `init`,
101+
// `init` contains contents and is safe to mutably borrow.
102+
let init = unsafe { ManuallyDrop::take(&mut *inner.init.get()) };
103+
let rust_start = init.init();
104+
rust_start();
107105

108106
// Fix the current thread's state just in case, so that the
109107
// destructors won't abort

library/std/src/sys/thread/teeos.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::mem::{self, ManuallyDrop};
22
use crate::sys::os;
3+
use crate::thread::ThreadInit;
34
use crate::time::Duration;
45
use crate::{cmp, io, ptr};
56

@@ -24,12 +25,8 @@ unsafe impl Sync for Thread {}
2425

2526
impl Thread {
2627
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
27-
pub unsafe fn new(
28-
stack: usize,
29-
_name: Option<&str>,
30-
p: Box<dyn FnOnce()>,
31-
) -> io::Result<Thread> {
32-
let p = Box::into_raw(Box::new(p));
28+
pub unsafe fn new(stack: usize, init: Box<ThreadInit>) -> io::Result<Thread> {
29+
let data = Box::into_raw(init);
3330
let mut native: libc::pthread_t = unsafe { mem::zeroed() };
3431
let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() };
3532
assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0);
@@ -62,16 +59,16 @@ impl Thread {
6259
}
6360
};
6461

65-
let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) };
66-
// Note: if the thread creation fails and this assert fails, then p will
62+
let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, data as *mut _) };
63+
// Note: if the thread creation fails and this assert fails, then data will
6764
// be leaked. However, an alternative design could cause double-free
6865
// which is clearly worse.
6966
assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0);
7067

7168
return if ret != 0 {
72-
// The thread failed to start and as a result p was not consumed. Therefore, it is
69+
// The thread failed to start and as a result data was not consumed. Therefore, it is
7370
// safe to reconstruct the box so that it gets deallocated.
74-
drop(unsafe { Box::from_raw(p) });
71+
drop(unsafe { Box::from_raw(data) });
7572
Err(io::Error::from_raw_os_error(ret))
7673
} else {
7774
// The new thread will start running earliest after the next yield.
@@ -80,15 +77,11 @@ impl Thread {
8077
Ok(Thread { id: native })
8178
};
8279

83-
extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
84-
unsafe {
85-
// Next, set up our stack overflow handler which may get triggered if we run
86-
// out of stack.
87-
// this is not necessary in TEE.
88-
//let _handler = stack_overflow::Handler::new();
89-
// Finally, let's run some code.
90-
Box::from_raw(main as *mut Box<dyn FnOnce()>)();
91-
}
80+
extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void {
81+
// SAFETY: we are simply recreating the box that was leaked earlier.
82+
let init = unsafe { Box::from_raw(data as *mut ThreadInit) };
83+
let rust_start = init.init();
84+
rust_start();
9285
ptr::null_mut()
9386
}
9487
}

0 commit comments

Comments
 (0)