Auto merge of #123550 - GnomedDev:remove-initial-arc, r=Noratrieb

Remove the `Arc` rt::init allocation for thread info

Removes an allocation pre-main by just not storing anything in std:🧵:Thread for the main thread.
- The thread name can just be a hard coded literal, as was done in #123433.
- Storing ThreadId and 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 commit is contained in:
bors 2024-10-24 13:35:50 +00:00
commit f61306d47b
5 changed files with 124 additions and 58 deletions

View File

@ -364,6 +364,7 @@
#![feature(std_internals)] #![feature(std_internals)]
#![feature(str_internals)] #![feature(str_internals)]
#![feature(strict_provenance_atomic_ptr)] #![feature(strict_provenance_atomic_ptr)]
#![feature(sync_unsafe_cell)]
#![feature(ub_checks)] #![feature(ub_checks)]
// tidy-alphabetical-end // tidy-alphabetical-end
// //

View File

@ -110,7 +110,7 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
// handle does not match the current ID, we should attempt to use the // handle does not match the current ID, we should attempt to use the
// current thread ID here instead of unconditionally creating a new // current thread ID here instead of unconditionally creating a new
// one. Also see #130210. // one. Also see #130210.
let thread = Thread::new_main(thread::current_id()); let thread = unsafe { Thread::new_main(thread::current_id()) };
if let Err(_thread) = thread::set_current(thread) { if let Err(_thread) = thread::set_current(thread) {
// `thread::current` will create a new handle if none has been set yet. // `thread::current` will create a new handle if none has been set yet.
// Thus, if someone uses it before main, this call will fail. That's a // Thus, if someone uses it before main, this call will fail. That's a

View File

@ -158,9 +158,12 @@
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] #[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests; mod tests;
use core::cell::SyncUnsafeCell;
use core::ffi::CStr;
use core::mem::MaybeUninit;
use crate::any::Any; use crate::any::Any;
use crate::cell::UnsafeCell; use crate::cell::UnsafeCell;
use crate::ffi::CStr;
use crate::marker::PhantomData; use crate::marker::PhantomData;
use crate::mem::{self, ManuallyDrop, forget}; use crate::mem::{self, ManuallyDrop, forget};
use crate::num::NonZero; use crate::num::NonZero;
@ -1125,7 +1128,7 @@ pub fn park_timeout(dur: Duration) {
let guard = PanicGuard; let guard = PanicGuard;
// SAFETY: park_timeout is called on the parker owned by this thread. // SAFETY: park_timeout is called on the parker owned by this thread.
unsafe { unsafe {
current().inner.as_ref().parker().park_timeout(dur); current().0.parker().park_timeout(dur);
} }
// No panic occurred, do not abort. // No panic occurred, do not abort.
forget(guard); forget(guard);
@ -1232,30 +1235,31 @@ pub fn as_u64(&self) -> NonZero<u64> {
// Thread // Thread
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// The internal representation of a `Thread`'s name.
enum ThreadName {
Main,
Other(ThreadNameString),
Unnamed,
}
// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. // This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
mod thread_name_string { mod thread_name_string {
use core::str; use core::str;
use super::ThreadName;
use crate::ffi::{CStr, CString}; use crate::ffi::{CStr, CString};
/// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
pub(crate) struct ThreadNameString { pub(crate) struct ThreadNameString {
inner: CString, inner: CString,
} }
impl ThreadNameString {
pub fn as_str(&self) -> &str {
// SAFETY: `self.inner` is only initialised via `String`, which upholds the validity invariant of `str`.
unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
}
}
impl core::ops::Deref for ThreadNameString { impl core::ops::Deref for ThreadNameString {
type Target = CStr; type Target = CStr;
fn deref(&self) -> &CStr { fn deref(&self) -> &CStr {
&self.inner &self.inner
} }
} }
impl From<String> for ThreadNameString { impl From<String> for ThreadNameString {
fn from(s: String) -> Self { fn from(s: String) -> Self {
Self { Self {
@ -1263,34 +1267,82 @@ fn from(s: String) -> Self {
} }
} }
} }
impl ThreadName {
pub fn as_cstr(&self) -> Option<&CStr> {
match self {
ThreadName::Main => Some(c"main"),
ThreadName::Other(other) => Some(other),
ThreadName::Unnamed => None,
}
}
pub fn as_str(&self) -> Option<&str> {
// SAFETY: `as_cstr` can only return `Some` for a fixed CStr or a `ThreadNameString`,
// which is guaranteed to be UTF-8.
self.as_cstr().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) })
}
}
} }
pub(crate) use thread_name_string::ThreadNameString; pub(crate) use thread_name_string::ThreadNameString;
/// The internal representation of a `Thread` handle static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit<ThreadId>, MaybeUninit<Parker>)> =
struct Inner { SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit()));
name: ThreadName, // Guaranteed to be UTF-8
/// The internal representation of a `Thread` that is not the main thread.
struct OtherInner {
name: Option<ThreadNameString>,
id: ThreadId, id: ThreadId,
parker: Parker, parker: Parker,
} }
/// The internal representation of a `Thread` handle.
#[derive(Clone)]
enum Inner {
/// Represents the main thread. May only be constructed by Thread::new_main.
Main(&'static (ThreadId, Parker)),
/// Represents any other thread.
Other(Pin<Arc<OtherInner>>),
}
impl Inner { impl Inner {
fn parker(self: Pin<&Self>) -> Pin<&Parker> { fn id(&self) -> ThreadId {
unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } match self {
Self::Main((thread_id, _)) => *thread_id,
Self::Other(other) => other.id,
}
}
fn cname(&self) -> Option<&CStr> {
match self {
Self::Main(_) => Some(c"main"),
Self::Other(other) => other.name.as_deref(),
}
}
fn name(&self) -> Option<&str> {
match self {
Self::Main(_) => Some("main"),
Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str),
}
}
fn into_raw(self) -> *const () {
match self {
// Just return the pointer to `MAIN_THREAD_INFO`.
Self::Main(ptr) => crate::ptr::from_ref(ptr).cast(),
Self::Other(arc) => {
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
let inner = unsafe { Pin::into_inner_unchecked(arc) };
Arc::into_raw(inner) as *const ()
}
}
}
/// # Safety
///
/// See [`Thread::from_raw`].
unsafe fn from_raw(ptr: *const ()) -> Self {
// If the pointer is to `MAIN_THREAD_INFO`, we know it is the `Main` variant.
if crate::ptr::eq(ptr.cast(), &MAIN_THREAD_INFO) {
Self::Main(unsafe { &*ptr.cast() })
} else {
// Safety: Upheld by caller
Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) })
}
}
fn parker(&self) -> Pin<&Parker> {
match self {
Self::Main((_, parker_ref)) => Pin::static_ref(parker_ref),
Self::Other(inner) => unsafe {
Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker)
},
}
} }
} }
@ -1314,33 +1366,47 @@ fn parker(self: Pin<&Self>) -> Pin<&Parker> {
/// docs of [`Builder`] and [`spawn`] for more details. /// docs of [`Builder`] and [`spawn`] for more details.
/// ///
/// [`thread::current`]: current::current /// [`thread::current`]: current::current
pub struct Thread { pub struct Thread(Inner);
inner: Pin<Arc<Inner>>,
}
impl Thread { impl Thread {
/// Used only internally to construct a thread object without spawning. /// Used only internally to construct a thread object without spawning.
pub(crate) fn new(id: ThreadId, name: String) -> Thread { pub(crate) fn new(id: ThreadId, name: String) -> Thread {
Self::new_inner(id, ThreadName::Other(name.into())) Self::new_inner(id, Some(ThreadNameString::from(name)))
} }
pub(crate) fn new_unnamed(id: ThreadId) -> Thread { pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
Self::new_inner(id, ThreadName::Unnamed) Self::new_inner(id, None)
} }
/// Constructs the thread handle for the main thread. /// Used in runtime to construct main thread
pub(crate) fn new_main(id: ThreadId) -> Thread { ///
Self::new_inner(id, ThreadName::Main) /// # Safety
///
/// This must only ever be called once, and must be called on the main thread.
pub(crate) unsafe fn new_main(thread_id: ThreadId) -> Thread {
// Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_THREAD_INFO
// as the only other read occurs in `main_thread_info` *after* the main thread has been constructed,
// and this function is the only one that constructs the main thread.
//
// Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread.
let main_thread_info = unsafe { &mut *MAIN_THREAD_INFO.get() };
unsafe { Parker::new_in_place((&raw mut main_thread_info.1).cast()) };
main_thread_info.0.write(thread_id);
// Store a `'static` ref to the initialised ThreadId and Parker,
// to avoid having to repeatedly prove initialisation.
Self(Inner::Main(unsafe { &*MAIN_THREAD_INFO.get().cast() }))
} }
fn new_inner(id: ThreadId, name: ThreadName) -> Thread { fn new_inner(id: ThreadId, name: Option<ThreadNameString>) -> Thread {
// We have to use `unsafe` here to construct the `Parker` in-place, // We have to use `unsafe` here to construct the `Parker` in-place,
// which is required for the UNIX implementation. // which is required for the UNIX implementation.
// //
// SAFETY: We pin the Arc immediately after creation, so its address never // SAFETY: We pin the Arc immediately after creation, so its address never
// changes. // changes.
let inner = unsafe { let inner = unsafe {
let mut arc = Arc::<Inner>::new_uninit(); let mut arc = Arc::<OtherInner>::new_uninit();
let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
(&raw mut (*ptr).name).write(name); (&raw mut (*ptr).name).write(name);
(&raw mut (*ptr).id).write(id); (&raw mut (*ptr).id).write(id);
@ -1348,7 +1414,7 @@ fn new_inner(id: ThreadId, name: ThreadName) -> Thread {
Pin::new_unchecked(arc.assume_init()) Pin::new_unchecked(arc.assume_init())
}; };
Thread { inner } Self(Inner::Other(inner))
} }
/// Like the public [`park`], but callable on any handle. This is used to /// Like the public [`park`], but callable on any handle. This is used to
@ -1357,7 +1423,7 @@ fn new_inner(id: ThreadId, name: ThreadName) -> Thread {
/// # Safety /// # Safety
/// May only be called from the thread to which this handle belongs. /// May only be called from the thread to which this handle belongs.
pub(crate) unsafe fn park(&self) { pub(crate) unsafe fn park(&self) {
unsafe { self.inner.as_ref().parker().park() } unsafe { self.0.parker().park() }
} }
/// Atomically makes the handle's token available if it is not already. /// Atomically makes the handle's token available if it is not already.
@ -1393,7 +1459,7 @@ pub(crate) unsafe fn park(&self) {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[inline] #[inline]
pub fn unpark(&self) { pub fn unpark(&self) {
self.inner.as_ref().parker().unpark(); self.0.parker().unpark();
} }
/// Gets the thread's unique identifier. /// Gets the thread's unique identifier.
@ -1413,7 +1479,7 @@ pub fn unpark(&self) {
#[stable(feature = "thread_id", since = "1.19.0")] #[stable(feature = "thread_id", since = "1.19.0")]
#[must_use] #[must_use]
pub fn id(&self) -> ThreadId { pub fn id(&self) -> ThreadId {
self.inner.id self.0.id()
} }
/// Gets the thread's name. /// Gets the thread's name.
@ -1456,7 +1522,11 @@ pub fn id(&self) -> ThreadId {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
#[must_use] #[must_use]
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&str> {
self.inner.name.as_str() self.0.name()
}
fn cname(&self) -> Option<&CStr> {
self.0.cname()
} }
/// Consumes the `Thread`, returning a raw pointer. /// Consumes the `Thread`, returning a raw pointer.
@ -1480,9 +1550,7 @@ pub fn name(&self) -> Option<&str> {
/// ``` /// ```
#[unstable(feature = "thread_raw", issue = "97523")] #[unstable(feature = "thread_raw", issue = "97523")]
pub fn into_raw(self) -> *const () { pub fn into_raw(self) -> *const () {
// Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. self.0.into_raw()
let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
Arc::into_raw(inner) as *const ()
} }
/// Constructs a `Thread` from a raw pointer. /// Constructs a `Thread` from a raw pointer.
@ -1504,11 +1572,7 @@ pub fn into_raw(self) -> *const () {
#[unstable(feature = "thread_raw", issue = "97523")] #[unstable(feature = "thread_raw", issue = "97523")]
pub unsafe fn from_raw(ptr: *const ()) -> Thread { pub unsafe fn from_raw(ptr: *const ()) -> Thread {
// Safety: Upheld by caller. // Safety: Upheld by caller.
unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } unsafe { Thread(Inner::from_raw(ptr)) }
}
fn cname(&self) -> Option<&CStr> {
self.inner.name.as_cstr()
} }
} }

View File

@ -12,15 +12,15 @@
// cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >] // cdb-check:join_handle,d [Type: std::thread::JoinHandle<tuple$<> >]
// cdb-check: [...] __0 [Type: std::thread::JoinInner<tuple$<> >] // cdb-check: [...] __0 [Type: std::thread::JoinInner<tuple$<> >]
// //
// cdb-command:dx t,d // cdb-command:dx -r3 t,d
// cdb-check:t,d : [...] [Type: std::thread::Thread *] // cdb-check:t,d : [...] [Type: std::thread::Thread *]
// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner,alloc::alloc::Global> >] // cdb-check: [...] __0 : Other [Type: enum2$<std::thread::Inner>]
// cdb-check: [...] __0 [Type: core::pin::Pin<alloc::sync::Arc<std::thread::OtherInner,[...]> >]
use std::thread; use std::thread;
#[allow(unused_variables)] #[allow(unused_variables)]
fn main() fn main() {
{
let join_handle = thread::spawn(|| { let join_handle = thread::spawn(|| {
println!("Initialize a thread"); println!("Initialize a thread");
}); });

View File

@ -40,6 +40,7 @@
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! drop(String::from("An allocation"));
//! assert!(unsafe { HIT }); //! assert!(unsafe { HIT });
//! } //! }
//! ``` //! ```