Skip to content

Commit ec82ace

Browse files
committed
Auto merge of rust-lang#123550 - GnomedDev:remove-initial-arc, r=Nilstrieb,saethlin
Remove last rt::init allocation for thread info Removes the last allocation pre-main by just not storing anything in std::thread::Thread for the main thread. - The thread name can just be a hard coded literal, as was done in rust-lang#123433. - The ThreadId is always the `1` value, so `ThreadId::new` now starts at `2` and can fabricate the `1` value when needed. - Storing Parker in a static that is initialized once at startup. This uses SyncUnsafeCell and MaybeUninit as this is quite performance critical and we don't need synchronization or to store a tag value and possibly leave in a panic. This also adds a UI test to make sure that allocations do not occur before main ever again.
2 parents 20aa2d8 + 519fe58 commit ec82ace

File tree

6 files changed

+151
-42
lines changed

6 files changed

+151
-42
lines changed

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@
356356
#![feature(str_internals)]
357357
#![feature(strict_provenance)]
358358
#![feature(strict_provenance_atomic_ptr)]
359+
#![feature(sync_unsafe_cell)]
359360
#![feature(ub_checks)]
360361
// tidy-alphabetical-end
361362
//

library/std/src/thread/mod.rs

+90-40
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,14 @@
159159
mod tests;
160160

161161
use crate::any::Any;
162+
use crate::cell::SyncUnsafeCell;
162163
use crate::cell::{OnceCell, UnsafeCell};
163164
use crate::env;
164165
use crate::ffi::{CStr, CString};
165166
use crate::fmt;
166167
use crate::io;
167168
use crate::marker::PhantomData;
169+
use crate::mem::MaybeUninit;
168170
use crate::mem::{self, forget};
169171
use crate::num::NonZero;
170172
use crate::panic;
@@ -530,7 +532,7 @@ impl Builder {
530532

531533
let f = MaybeDangling::new(f);
532534
let main = move || {
533-
if let Some(name) = their_thread.cname() {
535+
if let Some(name) = their_thread.0.name() {
534536
imp::Thread::set_name(name);
535537
}
536538

@@ -1162,7 +1164,7 @@ pub fn park_timeout(dur: Duration) {
11621164
let guard = PanicGuard;
11631165
// SAFETY: park_timeout is called on the parker owned by this thread.
11641166
unsafe {
1165-
current().inner.as_ref().parker().park_timeout(dur);
1167+
current().0.parker().park_timeout(dur);
11661168
}
11671169
// No panic occurred, do not abort.
11681170
forget(guard);
@@ -1201,7 +1203,12 @@ pub fn park_timeout(dur: Duration) {
12011203
pub struct ThreadId(NonZero<u64>);
12021204

12031205
impl ThreadId {
1204-
// Generate a new unique thread ID.
1206+
/// Generate a new unique thread ID.
1207+
///
1208+
/// The current implementation starts at 2 and increments from there.
1209+
///
1210+
/// This is as `1` is the value for the main thread, so std::thread::Thread does not
1211+
/// have to store this value when creating the main thread's information.
12051212
fn new() -> ThreadId {
12061213
#[cold]
12071214
fn exhausted() -> ! {
@@ -1212,7 +1219,7 @@ impl ThreadId {
12121219
if #[cfg(target_has_atomic = "64")] {
12131220
use crate::sync::atomic::AtomicU64;
12141221

1215-
static COUNTER: AtomicU64 = AtomicU64::new(0);
1222+
static COUNTER: AtomicU64 = AtomicU64::new(1);
12161223

12171224
let mut last = COUNTER.load(Ordering::Relaxed);
12181225
loop {
@@ -1228,7 +1235,7 @@ impl ThreadId {
12281235
} else {
12291236
use crate::sync::{Mutex, PoisonError};
12301237

1231-
static COUNTER: Mutex<u64> = Mutex::new(0);
1238+
static COUNTER: Mutex<u64> = Mutex::new(1);
12321239

12331240
let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner);
12341241
let Some(id) = counter.checked_add(1) else {
@@ -1245,6 +1252,11 @@ impl ThreadId {
12451252
}
12461253
}
12471254

1255+
/// Creates a ThreadId with the ID of the main thread.
1256+
fn new_main() -> Self {
1257+
Self(NonZero::<u64>::MIN)
1258+
}
1259+
12481260
/// This returns a numeric identifier for the thread identified by this
12491261
/// `ThreadId`.
12501262
///
@@ -1264,23 +1276,59 @@ impl ThreadId {
12641276
// Thread
12651277
////////////////////////////////////////////////////////////////////////////////
12661278

1267-
/// The internal representation of a `Thread`'s name.
1268-
enum ThreadName {
1269-
Main,
1270-
Other(CString),
1271-
Unnamed,
1272-
}
1279+
/// The parker for the main thread. This avoids having to allocate an Arc in `fn main() {}`.
1280+
static MAIN_PARKER: SyncUnsafeCell<MaybeUninit<Parker>> =
1281+
SyncUnsafeCell::new(MaybeUninit::uninit());
12731282

1274-
/// The internal representation of a `Thread` handle
1275-
struct Inner {
1276-
name: ThreadName, // Guaranteed to be UTF-8
1283+
/// The internal representation of a `Thread` that is not the main thread.
1284+
struct OtherInner {
1285+
name: Option<CString>, // Guaranteed to be UTF-8
12771286
id: ThreadId,
12781287
parker: Parker,
12791288
}
12801289

1290+
/// The internal representation of a `Thread` handle.
1291+
#[derive(Clone)]
1292+
enum Inner {
1293+
/// Represents the main thread. May only be constructed by Thread::new_main.
1294+
Main,
1295+
/// Represents any other thread.
1296+
Other(Pin<Arc<OtherInner>>),
1297+
}
1298+
12811299
impl Inner {
1282-
fn parker(self: Pin<&Self>) -> Pin<&Parker> {
1283-
unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
1300+
fn id(&self) -> ThreadId {
1301+
match self {
1302+
Self::Main => ThreadId::new_main(),
1303+
Self::Other(other) => other.id,
1304+
}
1305+
}
1306+
1307+
fn name(&self) -> Option<&CStr> {
1308+
match self {
1309+
Self::Main => Some(c"main"),
1310+
Self::Other(other) => other.name.as_deref(),
1311+
}
1312+
}
1313+
1314+
fn parker(&self) -> Pin<&Parker> {
1315+
match self {
1316+
Self::Main => {
1317+
// Safety: MAIN_PARKER is only ever read in this function, which requires access
1318+
// to an existing `&Inner` value, which can only be accessed via the main thread
1319+
// giving away such an instance from `current()`, implying that initialization,
1320+
// the only write to `MAIN_PARKER`, has been completed.
1321+
let static_ref: &'static MaybeUninit<Parker> = unsafe { &*MAIN_PARKER.get() };
1322+
1323+
// Safety: MAIN_PARKER is initialised when Inner::Main is initialised.
1324+
let parker_ref = unsafe { static_ref.assume_init_ref() };
1325+
1326+
Pin::static_ref(parker_ref)
1327+
}
1328+
Self::Other(inner) => unsafe {
1329+
Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker)
1330+
},
1331+
}
12841332
}
12851333
}
12861334

@@ -1304,46 +1352,56 @@ impl Inner {
13041352
/// docs of [`Builder`] and [`spawn`] for more details.
13051353
///
13061354
/// [`thread::current`]: current
1307-
pub struct Thread {
1308-
inner: Pin<Arc<Inner>>,
1309-
}
1355+
pub struct Thread(Inner);
13101356

13111357
impl Thread {
13121358
/// Used only internally to construct a thread object without spawning.
13131359
///
13141360
/// # Safety
13151361
/// `name` must be valid UTF-8.
13161362
pub(crate) unsafe fn new(name: CString) -> Thread {
1317-
unsafe { Self::new_inner(ThreadName::Other(name)) }
1363+
unsafe { Self::new_inner(Some(name)) }
13181364
}
13191365

13201366
pub(crate) fn new_unnamed() -> Thread {
1321-
unsafe { Self::new_inner(ThreadName::Unnamed) }
1367+
unsafe { Self::new_inner(None) }
13221368
}
13231369

1324-
// Used in runtime to construct main thread
1325-
pub(crate) fn new_main() -> Thread {
1326-
unsafe { Self::new_inner(ThreadName::Main) }
1370+
/// Used in runtime to construct main thread
1371+
///
1372+
/// # Safety
1373+
///
1374+
/// This must only ever be called once, and must be called on the main thread.
1375+
pub(crate) unsafe fn new_main() -> Thread {
1376+
// Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_PARKER
1377+
// as the only other read occurs in Inner::parker *after* Inner::Main has been constructed,
1378+
// and this function is the only one that constructs Inner::Main.
1379+
//
1380+
// Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread.
1381+
unsafe { Parker::new_in_place(MAIN_PARKER.get().cast()) }
1382+
1383+
Self(Inner::Main)
13271384
}
13281385

13291386
/// # Safety
1330-
/// If `name` is `ThreadName::Other(_)`, the contained string must be valid UTF-8.
1331-
unsafe fn new_inner(name: ThreadName) -> Thread {
1387+
///
1388+
/// If `name` is `Some(_)`, the contained string must be valid UTF-8.
1389+
unsafe fn new_inner(name: Option<CString>) -> Thread {
13321390
// We have to use `unsafe` here to construct the `Parker` in-place,
13331391
// which is required for the UNIX implementation.
13341392
//
13351393
// SAFETY: We pin the Arc immediately after creation, so its address never
13361394
// changes.
13371395
let inner = unsafe {
1338-
let mut arc = Arc::<Inner>::new_uninit();
1396+
let mut arc = Arc::<OtherInner>::new_uninit();
13391397
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
13401398
addr_of_mut!((*ptr).name).write(name);
13411399
addr_of_mut!((*ptr).id).write(ThreadId::new());
13421400
Parker::new_in_place(addr_of_mut!((*ptr).parker));
13431401
Pin::new_unchecked(arc.assume_init())
13441402
};
13451403

1346-
Thread { inner }
1404+
Self(Inner::Other(inner))
13471405
}
13481406

13491407
/// Like the public [`park`], but callable on any handle. This is used to
@@ -1352,7 +1410,7 @@ impl Thread {
13521410
/// # Safety
13531411
/// May only be called from the thread to which this handle belongs.
13541412
pub(crate) unsafe fn park(&self) {
1355-
unsafe { self.inner.as_ref().parker().park() }
1413+
unsafe { self.0.parker().park() }
13561414
}
13571415

13581416
/// Atomically makes the handle's token available if it is not already.
@@ -1388,7 +1446,7 @@ impl Thread {
13881446
#[stable(feature = "rust1", since = "1.0.0")]
13891447
#[inline]
13901448
pub fn unpark(&self) {
1391-
self.inner.as_ref().parker().unpark();
1449+
self.0.parker().unpark();
13921450
}
13931451

13941452
/// Gets the thread's unique identifier.
@@ -1408,7 +1466,7 @@ impl Thread {
14081466
#[stable(feature = "thread_id", since = "1.19.0")]
14091467
#[must_use]
14101468
pub fn id(&self) -> ThreadId {
1411-
self.inner.id
1469+
self.0.id()
14121470
}
14131471

14141472
/// Gets the thread's name.
@@ -1451,15 +1509,7 @@ impl Thread {
14511509
#[stable(feature = "rust1", since = "1.0.0")]
14521510
#[must_use]
14531511
pub fn name(&self) -> Option<&str> {
1454-
self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) })
1455-
}
1456-
1457-
fn cname(&self) -> Option<&CStr> {
1458-
match &self.inner.name {
1459-
ThreadName::Main => Some(c"main"),
1460-
ThreadName::Other(other) => Some(&other),
1461-
ThreadName::Unnamed => None,
1462-
}
1512+
self.0.name().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) })
14631513
}
14641514
}
14651515

tests/debuginfo/thread.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
// cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >]
1313
// cdb-check: [...] __0 [Type: std::thread::JoinInner<tuple$<> >]
1414
//
15-
// cdb-command:dx t,d
15+
// cdb-command:dx -r3 t,d
1616
// cdb-check:t,d : [...] [Type: std::thread::Thread *]
17-
// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner,alloc::alloc::Global> >]
17+
// cdb-check: [...] __0 : Other [Type: enum2$<std::thread::Inner>]
18+
// cdb-check: [...] __0 [Type: core::pin::Pin<alloc::sync::Arc<std::thread::OtherInner,[...]> >]
1819

1920
use std::thread;
2021

tests/rustdoc/demo-allocator-54478.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
//! }
4141
//!
4242
//! fn main() {
43+
//! drop(String::from("An allocation"));
4344
//! assert!(unsafe { HIT });
4445
//! }
4546
//! ```

tests/ui/runtime/aborting-alloc.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! Helper for 'no-allocation-before-main'.
2+
//!
3+
//! This also contains a meta-test to make sure that the AbortingAllocator does indeed abort.
4+
//!
5+
//! -Cprefer-dynamic=no is required as otherwise #[global_allocator] does nothing.
6+
//@ run-fail
7+
//@ compile-flags: -Cprefer-dynamic=no
8+
9+
use std::{sync::atomic::{AtomicBool, Ordering}, alloc::System};
10+
11+
static ABORT: AtomicBool = AtomicBool::new(true);
12+
13+
pub struct AbortingAllocator(System);
14+
15+
unsafe impl std::alloc::GlobalAlloc for AbortingAllocator {
16+
unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
17+
if ABORT.swap(false, Ordering::SeqCst) {
18+
println!("{}", std::backtrace::Backtrace::force_capture());
19+
std::process::abort();
20+
}
21+
22+
self.0.alloc(layout)
23+
}
24+
25+
unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
26+
if ABORT.swap(false, Ordering::SeqCst) {
27+
println!("{}", std::backtrace::Backtrace::force_capture());
28+
std::process::abort();
29+
}
30+
31+
self.0.dealloc(ptr, layout)
32+
}
33+
}
34+
35+
#[global_allocator]
36+
static ALLOCATOR: AbortingAllocator = AbortingAllocator(System);
37+
38+
fn main() {
39+
std::hint::black_box(String::from("An allocation"));
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! Tests that a program with no body does not allocate.
2+
//!
3+
//! The initial runtime should not allocate for performance/binary size reasons.
4+
//!
5+
//! -Cprefer-dynamic=no is required as otherwise #[global_allocator] does nothing.
6+
//! We only test linux-gnu as other targets currently need allocation for thread dtors.
7+
//@ run-pass
8+
//@ compile-flags: -Cprefer-dynamic=no -Cdebuginfo=full
9+
//@ only-linux
10+
//@ only-gnu
11+
12+
#[allow(dead_code)]
13+
#[path = "aborting-alloc.rs"]
14+
mod aux;
15+
16+
fn main() {}

0 commit comments

Comments
 (0)