about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/env.rs15
-rw-r--r--library/std/src/ffi/os_str.rs12
-rw-r--r--library/std/src/fs.rs7
-rw-r--r--library/std/src/io/cursor.rs6
-rw-r--r--library/std/src/lib.rs6
-rw-r--r--library/std/src/os/fd/owned.rs21
-rw-r--r--library/std/src/path.rs1
-rw-r--r--library/std/src/prelude/common.rs3
-rw-r--r--library/std/src/sync/mpmc/array.rs6
-rw-r--r--library/std/src/sync/mpmc/context.rs13
-rw-r--r--library/std/src/sync/mpmc/list.rs3
-rw-r--r--library/std/src/sync/mpmc/zero.rs6
-rw-r--r--library/std/src/sync/rwlock/tests.rs5
-rw-r--r--library/std/src/sys/pal/uefi/process.rs68
-rw-r--r--library/std/src/sys/pal/unix/os.rs78
-rw-r--r--library/std/src/sys/pal/unix/os/tests.rs25
-rw-r--r--library/std/src/sys/random/arc4random.rs2
-rw-r--r--library/std/src/sys/sync/once/queue.rs9
-rw-r--r--library/std/src/sys/sync/rwlock/queue.rs6
-rw-r--r--library/std/src/thread/current.rs31
-rw-r--r--library/std/src/thread/mod.rs49
-rw-r--r--library/std/src/thread/scoped.rs9
-rw-r--r--library/std/src/thread/spawnhook.rs148
23 files changed, 448 insertions, 81 deletions
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index d732a15117e..27f4daba44b 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -653,19 +653,28 @@ pub fn home_dir() -> Option<PathBuf> {
 /// may result in "insecure temporary file" security vulnerabilities. Consider
 /// using a crate that securely creates temporary files or directories.
 ///
+/// Note that the returned value may be a symbolic link, not a directory.
+///
 /// # Platform-specific behavior
 ///
 /// On Unix, returns the value of the `TMPDIR` environment variable if it is
-/// set, otherwise for non-Android it returns `/tmp`. On Android, since there
-/// is no global temporary folder (it is usually allocated per-app), it returns
-/// `/data/local/tmp`.
+/// set, otherwise the value is OS-specific:
+/// - On Android, there is no global temporary folder (it is usually allocated
+///   per-app), it returns `/data/local/tmp`.
+/// - On Darwin-based OSes (macOS, iOS, etc) it returns the directory provided
+///   by `confstr(_CS_DARWIN_USER_TEMP_DIR, ...)`, as recommended by [Apple's
+///   security guidelines][appledoc].
+/// - On all other unix-based OSes, it returns `/tmp`.
+///
 /// On Windows, the behavior is equivalent to that of [`GetTempPath2`][GetTempPath2] /
 /// [`GetTempPath`][GetTempPath], which this function uses internally.
+///
 /// Note that, this [may change in the future][changes].
 ///
 /// [changes]: io#platform-specific-behavior
 /// [GetTempPath2]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a
 /// [GetTempPath]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha
+/// [appledoc]: https://developer.apple.com/library/archive/documentation/Security/Conceptual/SecureCodingGuide/Articles/RaceConditions.html#//apple_ref/doc/uid/TP40002585-SW10
 ///
 /// ```no_run
 /// use std::env;
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 79dfb47d0c4..328185d1f2b 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -550,11 +550,15 @@ impl OsString {
         OsStr::from_inner_mut(self.inner.leak())
     }
 
-    /// Provides plumbing to core `Vec::truncate`.
-    /// More well behaving alternative to allowing outer types
-    /// full mutable access to the core `Vec`.
+    /// Truncate the the `OsString` to the specified length.
+    ///
+    /// # Panics
+    /// Panics if `len` does not lie on a valid `OsStr` boundary
+    /// (as described in [`OsStr::slice_encoded_bytes`]).
     #[inline]
-    pub(crate) fn truncate(&mut self, len: usize) {
+    #[unstable(feature = "os_string_truncate", issue = "133262")]
+    pub fn truncate(&mut self, len: usize) {
+        self.as_os_str().inner.check_public_boundary(len);
         self.inner.truncate(len);
     }
 
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 76fdb1a4ffd..d846a4e5f09 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2738,6 +2738,10 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 
 /// Removes an empty directory.
 ///
+/// If you want to remove a directory that is not empty, as well as all
+/// of its contents recursively, consider using [`remove_dir_all`]
+/// instead.
+///
 /// # Platform-specific behavior
 ///
 /// This function currently corresponds to the `rmdir` function on Unix
@@ -2800,8 +2804,9 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 ///
 /// See [`fs::remove_file`] and [`fs::remove_dir`].
 ///
-/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root path.
+/// `remove_dir_all` will fail if `remove_dir` or `remove_file` fail on any constituent paths, including the root `path`.
 /// As a result, the directory you are deleting must exist, meaning that this function is not idempotent.
+/// Additionally, `remove_dir_all` will also fail if the `path` is not a directory.
 ///
 /// Consider ignoring the error if validating the removal is not required for your use case.
 ///
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index 9f913eae095..fbfdb4fa023 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -153,7 +153,8 @@ impl<T> Cursor<T> {
     /// let reference = buff.get_mut();
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn get_mut(&mut self) -> &mut T {
+    #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")]
+    pub const fn get_mut(&mut self) -> &mut T {
         &mut self.inner
     }
 
@@ -200,7 +201,8 @@ impl<T> Cursor<T> {
     /// assert_eq!(buff.position(), 4);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn set_position(&mut self, pos: u64) {
+    #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")]
+    pub const fn set_position(&mut self, pos: u64) {
         self.pos = pos;
     }
 }
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 5b94f036248..ee6fceb024f 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -174,9 +174,6 @@
 //!
 //! - after-main use of thread-locals, which also affects additional features:
 //!   - [`thread::current()`]
-//!   - [`thread::scope()`]
-//!   - [`sync::mpmc`]
-//!   - [`sync::mpsc`]
 //! - before-main stdio file descriptors are not guaranteed to be open on unix platforms
 //!
 //!
@@ -288,7 +285,6 @@
 #![feature(cfg_target_thread_local)]
 #![feature(cfi_encoding)]
 #![feature(concat_idents)]
-#![feature(const_float_methods)]
 #![feature(decl_macro)]
 #![feature(deprecated_suggestion)]
 #![feature(doc_cfg)]
@@ -658,6 +654,8 @@ pub mod arch {
     pub use std_detect::is_aarch64_feature_detected;
     #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
     pub use std_detect::is_arm_feature_detected;
+    #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")]
+    pub use std_detect::is_loongarch_feature_detected;
     #[unstable(feature = "is_riscv_feature_detected", issue = "111192")]
     pub use std_detect::is_riscv_feature_detected;
     #[stable(feature = "simd_x86", since = "1.27.0")]
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 2d087c03b04..388b8a88a1a 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -173,16 +173,17 @@ impl Drop for OwnedFd {
     #[inline]
     fn drop(&mut self) {
         unsafe {
-            // Note that errors are ignored when closing a file descriptor. The
-            // reason for this is that if an error occurs we don't actually know if
-            // the file descriptor was closed or not, and if we retried (for
-            // something like EINTR), we might close another valid file descriptor
-            // opened after we closed ours.
-            // However, this is usually justified, as some of the major Unices
-            // do make sure to always close the FD, even when `close()` is interrupted,
-            // and the scenario is rare to begin with.
-            // Helpful link to an epic discussion by POSIX workgroup:
-            // http://austingroupbugs.net/view.php?id=529
+            // Note that errors are ignored when closing a file descriptor. According to POSIX 2024,
+            // we can and indeed should retry `close` on `EINTR`
+            // (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/close.html),
+            // but it is not clear yet how well widely-used implementations are conforming with this
+            // mandate since older versions of POSIX left the state of the FD after an `EINTR`
+            // unspecified. Ignoring errors is "fine" because some of the major Unices (in
+            // particular, Linux) do make sure to always close the FD, even when `close()` is
+            // interrupted, and the scenario is rare to begin with. If we retried on a
+            // not-POSIX-compliant implementation, the consequences could be really bad since we may
+            // close the wrong FD. Helpful link to an epic discussion by POSIX workgroup that led to
+            // the latest POSIX wording: http://austingroupbugs.net/view.php?id=529
             #[cfg(not(target_os = "hermit"))]
             {
                 #[cfg(unix)]
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index b0291e3aa19..7ffb11b6aed 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2504,6 +2504,7 @@ impl Path {
     /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new("")));
     ///
     /// assert!(path.strip_prefix("test").is_err());
+    /// assert!(path.strip_prefix("/te").is_err());
     /// assert!(path.strip_prefix("/haha").is_err());
     ///
     /// let prefix = PathBuf::from("/test/");
diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs
index b231bd871b3..e4731280ffe 100644
--- a/library/std/src/prelude/common.rs
+++ b/library/std/src/prelude/common.rs
@@ -12,6 +12,9 @@ pub use crate::marker::{Send, Sized, Sync, Unpin};
 #[stable(feature = "rust1", since = "1.0.0")]
 #[doc(no_inline)]
 pub use crate::ops::{Drop, Fn, FnMut, FnOnce};
+#[unstable(feature = "async_closure", issue = "62290")]
+#[doc(no_inline)]
+pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
 
 // Re-exported functions
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs
index 2c8ba411f30..a467237fef1 100644
--- a/library/std/src/sync/mpmc/array.rs
+++ b/library/std/src/sync/mpmc/array.rs
@@ -346,7 +346,8 @@ impl<T> Channel<T> {
                 }
 
                 // Block the current thread.
-                let sel = cx.wait_until(deadline);
+                // SAFETY: the context belongs to the current thread.
+                let sel = unsafe { cx.wait_until(deadline) };
 
                 match sel {
                     Selected::Waiting => unreachable!(),
@@ -397,7 +398,8 @@ impl<T> Channel<T> {
                 }
 
                 // Block the current thread.
-                let sel = cx.wait_until(deadline);
+                // SAFETY: the context belongs to the current thread.
+                let sel = unsafe { cx.wait_until(deadline) };
 
                 match sel {
                     Selected::Waiting => unreachable!(),
diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs
index 2371d32d4ea..51aa7e82e78 100644
--- a/library/std/src/sync/mpmc/context.rs
+++ b/library/std/src/sync/mpmc/context.rs
@@ -69,7 +69,7 @@ impl Context {
             inner: Arc::new(Inner {
                 select: AtomicUsize::new(Selected::Waiting.into()),
                 packet: AtomicPtr::new(ptr::null_mut()),
-                thread: thread::current(),
+                thread: thread::current_or_unnamed(),
                 thread_id: current_thread_id(),
             }),
         }
@@ -112,8 +112,11 @@ impl Context {
     /// Waits until an operation is selected and returns it.
     ///
     /// If the deadline is reached, `Selected::Aborted` will be selected.
+    ///
+    /// # Safety
+    /// This may only be called from the thread this `Context` belongs to.
     #[inline]
-    pub fn wait_until(&self, deadline: Option<Instant>) -> Selected {
+    pub unsafe fn wait_until(&self, deadline: Option<Instant>) -> Selected {
         loop {
             // Check whether an operation has been selected.
             let sel = Selected::from(self.inner.select.load(Ordering::Acquire));
@@ -126,7 +129,8 @@ impl Context {
                 let now = Instant::now();
 
                 if now < end {
-                    thread::park_timeout(end - now);
+                    // SAFETY: guaranteed by caller.
+                    unsafe { self.inner.thread.park_timeout(end - now) };
                 } else {
                     // The deadline has been reached. Try aborting select.
                     return match self.try_select(Selected::Aborted) {
@@ -135,7 +139,8 @@ impl Context {
                     };
                 }
             } else {
-                thread::park();
+                // SAFETY: guaranteed by caller.
+                unsafe { self.inner.thread.park() };
             }
         }
     }
diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs
index 523e6d2f3bb..d88914f5291 100644
--- a/library/std/src/sync/mpmc/list.rs
+++ b/library/std/src/sync/mpmc/list.rs
@@ -444,7 +444,8 @@ impl<T> Channel<T> {
                 }
 
                 // Block the current thread.
-                let sel = cx.wait_until(deadline);
+                // SAFETY: the context belongs to the current thread.
+                let sel = unsafe { cx.wait_until(deadline) };
 
                 match sel {
                     Selected::Waiting => unreachable!(),
diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs
index 446881291e6..577997c07a6 100644
--- a/library/std/src/sync/mpmc/zero.rs
+++ b/library/std/src/sync/mpmc/zero.rs
@@ -190,7 +190,8 @@ impl<T> Channel<T> {
             drop(inner);
 
             // Block the current thread.
-            let sel = cx.wait_until(deadline);
+            // SAFETY: the context belongs to the current thread.
+            let sel = unsafe { cx.wait_until(deadline) };
 
             match sel {
                 Selected::Waiting => unreachable!(),
@@ -257,7 +258,8 @@ impl<T> Channel<T> {
             drop(inner);
 
             // Block the current thread.
-            let sel = cx.wait_until(deadline);
+            // SAFETY: the context belongs to the current thread.
+            let sel = unsafe { cx.wait_until(deadline) };
 
             match sel {
                 Selected::Waiting => unreachable!(),
diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs
index 29cad4400f1..48d442921f7 100644
--- a/library/std/src/sync/rwlock/tests.rs
+++ b/library/std/src/sync/rwlock/tests.rs
@@ -511,12 +511,15 @@ fn test_downgrade_basic() {
 }
 
 #[test]
+// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue.
+// See <https://github.com/rust-lang/rust/issues/121950> for details.
+#[cfg_attr(all(miri, target_os = "macos"), ignore)]
 fn test_downgrade_observe() {
     // Taken from the test `test_rwlock_downgrade` from:
     // https://github.com/Amanieu/parking_lot/blob/master/src/rwlock.rs
 
     const W: usize = 20;
-    const N: usize = 100;
+    const N: usize = if cfg!(miri) { 40 } else { 100 };
 
     // This test spawns `W` writer threads, where each will increment a counter `N` times, ensuring
     // that the value they wrote has not changed after downgrading.
diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs
index 0cc9cecb89d..1b83f4b0aee 100644
--- a/library/std/src/sys/pal/uefi/process.rs
+++ b/library/std/src/sys/pal/uefi/process.rs
@@ -18,6 +18,7 @@ use crate::{fmt, io};
 #[derive(Debug)]
 pub struct Command {
     prog: OsString,
+    args: Vec<OsString>,
     stdout: Option<Stdio>,
     stderr: Option<Stdio>,
 }
@@ -39,12 +40,11 @@ pub enum Stdio {
 
 impl Command {
     pub fn new(program: &OsStr) -> Command {
-        Command { prog: program.to_os_string(), stdout: None, stderr: None }
+        Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None }
     }
 
-    // FIXME: Implement arguments as reverse of parsing algorithm
-    pub fn arg(&mut self, _arg: &OsStr) {
-        panic!("unsupported")
+    pub fn arg(&mut self, arg: &OsStr) {
+        self.args.push(arg.to_os_string());
     }
 
     pub fn env_mut(&mut self) -> &mut CommandEnv {
@@ -72,7 +72,7 @@ impl Command {
     }
 
     pub fn get_args(&self) -> CommandArgs<'_> {
-        panic!("unsupported")
+        CommandArgs { iter: self.args.iter() }
     }
 
     pub fn get_envs(&self) -> CommandEnvs<'_> {
@@ -116,6 +116,12 @@ impl Command {
     pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
         let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?;
 
+        // UEFI adds the bin name by default
+        if !self.args.is_empty() {
+            let args = uefi_command_internal::create_args(&self.prog, &self.args);
+            cmd.set_args(args);
+        }
+
         // Setup Stdout
         let stdout = self.stdout.unwrap_or(Stdio::MakePipe);
         let stdout = Self::create_pipe(stdout)?;
@@ -315,7 +321,7 @@ mod uefi_command_internal {
         stdout: Option<helpers::OwnedProtocol<PipeProtocol>>,
         stderr: Option<helpers::OwnedProtocol<PipeProtocol>>,
         st: OwnedTable<r_efi::efi::SystemTable>,
-        args: Option<Vec<u16>>,
+        args: Option<(*mut u16, usize)>,
     }
 
     impl Image {
@@ -449,20 +455,20 @@ mod uefi_command_internal {
             }
         }
 
-        pub fn set_args(&mut self, args: &OsStr) {
+        pub fn set_args(&mut self, args: Box<[u16]>) {
             let loaded_image: NonNull<loaded_image::Protocol> =
                 helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap();
 
-            let mut args = args.encode_wide().collect::<Vec<u16>>();
-            let args_size = (crate::mem::size_of::<u16>() * args.len()) as u32;
+            let len = args.len();
+            let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap();
+            let ptr = Box::into_raw(args).as_mut_ptr();
 
             unsafe {
-                (*loaded_image.as_ptr()).load_options =
-                    args.as_mut_ptr() as *mut crate::ffi::c_void;
+                (*loaded_image.as_ptr()).load_options = ptr as *mut crate::ffi::c_void;
                 (*loaded_image.as_ptr()).load_options_size = args_size;
             }
 
-            self.args = Some(args);
+            self.args = Some((ptr, len));
         }
 
         fn update_st_crc32(&mut self) -> io::Result<()> {
@@ -502,6 +508,10 @@ mod uefi_command_internal {
                     ((*bt.as_ptr()).unload_image)(self.handle.as_ptr());
                 }
             }
+
+            if let Some((ptr, len)) = self.args {
+                let _ = unsafe { Box::from_raw(crate::ptr::slice_from_raw_parts_mut(ptr, len)) };
+            }
         }
     }
 
@@ -681,4 +691,38 @@ mod uefi_command_internal {
             }
         }
     }
+
+    pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> {
+        const QUOTE: u16 = 0x0022;
+        const SPACE: u16 = 0x0020;
+        const CARET: u16 = 0x005e;
+        const NULL: u16 = 0;
+
+        // This is the lower bound on the final length under the assumption that
+        // the arguments only contain ASCII characters.
+        let mut res = Vec::with_capacity(args.iter().map(|arg| arg.len() + 3).sum());
+
+        // Wrap program name in quotes to avoid any problems
+        res.push(QUOTE);
+        res.extend(prog.encode_wide());
+        res.push(QUOTE);
+        res.push(SPACE);
+
+        for arg in args {
+            // Wrap the argument in quotes to be treat as single arg
+            res.push(QUOTE);
+            for c in arg.encode_wide() {
+                // CARET in quotes is used to escape CARET or QUOTE
+                if c == QUOTE || c == CARET {
+                    res.push(CARET);
+                }
+                res.push(c);
+            }
+            res.push(QUOTE);
+
+            res.push(SPACE);
+        }
+
+        res.into_boxed_slice()
+    }
 }
diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs
index f983d174ed6..f207131ddf3 100644
--- a/library/std/src/sys/pal/unix/os.rs
+++ b/library/std/src/sys/pal/unix/os.rs
@@ -698,12 +698,82 @@ pub fn page_size() -> usize {
     unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
 }
 
+// Returns the value for [`confstr(key, ...)`][posix_confstr]. Currently only
+// used on Darwin, but should work on any unix (in case we need to get
+// `_CS_PATH` or `_CS_V[67]_ENV` in the future).
+//
+// [posix_confstr]:
+//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html
+//
+// FIXME: Support `confstr` in Miri.
+#[cfg(all(target_vendor = "apple", not(miri)))]
+fn confstr(key: c_int, size_hint: Option<usize>) -> io::Result<OsString> {
+    let mut buf: Vec<u8> = Vec::with_capacity(0);
+    let mut bytes_needed_including_nul = size_hint
+        .unwrap_or_else(|| {
+            // Treat "None" as "do an extra call to get the length". In theory
+            // we could move this into the loop below, but it's hard to do given
+            // that it isn't 100% clear if it's legal to pass 0 for `len` when
+            // the buffer isn't null.
+            unsafe { libc::confstr(key, core::ptr::null_mut(), 0) }
+        })
+        .max(1);
+    // If the value returned by `confstr` is greater than the len passed into
+    // it, then the value was truncated, meaning we need to retry. Note that
+    // while `confstr` results don't seem to change for a process, it's unclear
+    // if this is guaranteed anywhere, so looping does seem required.
+    while bytes_needed_including_nul > buf.capacity() {
+        // We write into the spare capacity of `buf`. This lets us avoid
+        // changing buf's `len`, which both simplifies `reserve` computation,
+        // allows working with `Vec<u8>` instead of `Vec<MaybeUninit<u8>>`, and
+        // may avoid a copy, since the Vec knows that none of the bytes are needed
+        // when reallocating (well, in theory anyway).
+        buf.reserve(bytes_needed_including_nul);
+        // `confstr` returns
+        // - 0 in the case of errors: we break and return an error.
+        // - The number of bytes written, iff the provided buffer is enough to
+        //   hold the entire value: we break and return the data in `buf`.
+        // - Otherwise, the number of bytes needed (including nul): we go
+        //   through the loop again.
+        bytes_needed_including_nul =
+            unsafe { libc::confstr(key, buf.as_mut_ptr().cast::<c_char>(), buf.capacity()) };
+    }
+    // `confstr` returns 0 in the case of an error.
+    if bytes_needed_including_nul == 0 {
+        return Err(io::Error::last_os_error());
+    }
+    // Safety: `confstr(..., buf.as_mut_ptr(), buf.capacity())` returned a
+    // non-zero value, meaning `bytes_needed_including_nul` bytes were
+    // initialized.
+    unsafe {
+        buf.set_len(bytes_needed_including_nul);
+        // Remove the NUL-terminator.
+        let last_byte = buf.pop();
+        // ... and smoke-check that it *was* a NUL-terminator.
+        assert_eq!(last_byte, Some(0), "`confstr` provided a string which wasn't nul-terminated");
+    };
+    Ok(OsString::from_vec(buf))
+}
+
+#[cfg(all(target_vendor = "apple", not(miri)))]
+fn darwin_temp_dir() -> PathBuf {
+    confstr(libc::_CS_DARWIN_USER_TEMP_DIR, Some(64)).map(PathBuf::from).unwrap_or_else(|_| {
+        // It failed for whatever reason (there are several possible reasons),
+        // so return the global one.
+        PathBuf::from("/tmp")
+    })
+}
+
 pub fn temp_dir() -> PathBuf {
     crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
-        if cfg!(target_os = "android") {
-            PathBuf::from("/data/local/tmp")
-        } else {
-            PathBuf::from("/tmp")
+        cfg_if::cfg_if! {
+            if #[cfg(all(target_vendor = "apple", not(miri)))] {
+                darwin_temp_dir()
+            } else if #[cfg(target_os = "android")] {
+                PathBuf::from("/data/local/tmp")
+            } else {
+                PathBuf::from("/tmp")
+            }
         }
     })
 }
diff --git a/library/std/src/sys/pal/unix/os/tests.rs b/library/std/src/sys/pal/unix/os/tests.rs
index efc29955b05..63a1cc1e94a 100644
--- a/library/std/src/sys/pal/unix/os/tests.rs
+++ b/library/std/src/sys/pal/unix/os/tests.rs
@@ -21,3 +21,28 @@ fn test_parse_glibc_version() {
         assert_eq!(parsed, super::parse_glibc_version(version_str));
     }
 }
+
+// Smoke check `confstr`, do it for several hint values, to ensure our resizing
+// logic is correct.
+#[test]
+#[cfg(all(target_vendor = "apple", not(miri)))]
+fn test_confstr() {
+    for key in [libc::_CS_DARWIN_USER_TEMP_DIR, libc::_CS_PATH] {
+        let value_nohint = super::confstr(key, None).unwrap_or_else(|e| {
+            panic!("confstr({key}, None) failed: {e:?}");
+        });
+        let end = (value_nohint.len() + 1) * 2;
+        for hint in 0..end {
+            assert_eq!(
+                super::confstr(key, Some(hint)).as_deref().ok(),
+                Some(&*value_nohint),
+                "confstr({key}, Some({hint})) failed",
+            );
+        }
+    }
+    // Smoke check that we don't loop forever or something if the input was not valid.
+    for hint in [None, Some(0), Some(1)] {
+        let hopefully_invalid = 123456789_i32;
+        assert!(super::confstr(hopefully_invalid, hint).is_err());
+    }
+}
diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs
index ffabaafbee8..32467e9ebaa 100644
--- a/library/std/src/sys/random/arc4random.rs
+++ b/library/std/src/sys/random/arc4random.rs
@@ -12,7 +12,6 @@
 #[cfg(not(any(
     target_os = "haiku",
     target_os = "illumos",
-    target_os = "rtems",
     target_os = "solaris",
     target_os = "vita",
 )))]
@@ -22,7 +21,6 @@ use libc::arc4random_buf;
 #[cfg(any(
     target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
     target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
-    target_os = "rtems", // See https://docs.rtems.org/branches/master/bsp-howto/getentropy.html
     target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
     target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
 ))]
diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs
index 177d0d7744a..87837915b39 100644
--- a/library/std/src/sys/sync/once/queue.rs
+++ b/library/std/src/sys/sync/once/queue.rs
@@ -93,7 +93,7 @@ const QUEUE_MASK: usize = !STATE_MASK;
 // use interior mutability.
 #[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
 struct Waiter {
-    thread: Cell<Option<Thread>>,
+    thread: Thread,
     signaled: AtomicBool,
     next: Cell<*const Waiter>,
 }
@@ -238,7 +238,7 @@ fn wait(
     return_on_poisoned: bool,
 ) -> StateAndQueue {
     let node = &Waiter {
-        thread: Cell::new(Some(thread::current())),
+        thread: thread::current_or_unnamed(),
         signaled: AtomicBool::new(false),
         next: Cell::new(ptr::null()),
     };
@@ -277,7 +277,8 @@ fn wait(
             // can park ourselves, the result could be this thread never gets
             // unparked. Luckily `park` comes with the guarantee that if it got
             // an `unpark` just before on an unparked thread it does not park.
-            thread::park();
+            // SAFETY: we retrieved this handle on the current thread above.
+            unsafe { node.thread.park() }
         }
 
         return state_and_queue.load(Acquire);
@@ -309,7 +310,7 @@ impl Drop for WaiterQueue<'_> {
             let mut queue = to_queue(current);
             while !queue.is_null() {
                 let next = (*queue).next.get();
-                let thread = (*queue).thread.take().unwrap();
+                let thread = (*queue).thread.clone();
                 (*queue).signaled.store(true, Release);
                 thread.unpark();
                 queue = next;
diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs
index 51330f8fafe..bd15f8ee952 100644
--- a/library/std/src/sys/sync/rwlock/queue.rs
+++ b/library/std/src/sys/sync/rwlock/queue.rs
@@ -118,7 +118,7 @@ use crate::mem;
 use crate::ptr::{self, NonNull, null_mut, without_provenance_mut};
 use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
 use crate::sync::atomic::{AtomicBool, AtomicPtr};
-use crate::thread::{self, Thread, ThreadId};
+use crate::thread::{self, Thread};
 
 /// The atomic lock state.
 type AtomicState = AtomicPtr<()>;
@@ -217,9 +217,7 @@ impl Node {
     /// Prepare this node for waiting.
     fn prepare(&mut self) {
         // Fall back to creating an unnamed `Thread` handle to allow locking in TLS destructors.
-        self.thread.get_or_init(|| {
-            thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new()))
-        });
+        self.thread.get_or_init(thread::current_or_unnamed);
         self.completed = AtomicBool::new(false);
     }
 
diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs
index e6eb90c4c30..1048ef97356 100644
--- a/library/std/src/thread/current.rs
+++ b/library/std/src/thread/current.rs
@@ -165,6 +165,23 @@ pub(crate) fn try_current() -> Option<Thread> {
     }
 }
 
+/// Gets a handle to the thread that invokes it. If the handle stored in thread-
+/// local storage was already destroyed, this creates a new unnamed temporary
+/// handle to allow thread parking in nearly all situations.
+pub(crate) fn current_or_unnamed() -> Thread {
+    let current = CURRENT.get();
+    if current > DESTROYED {
+        unsafe {
+            let current = ManuallyDrop::new(Thread::from_raw(current));
+            (*current).clone()
+        }
+    } else if current == DESTROYED {
+        Thread::new_unnamed(id::get_or_init())
+    } else {
+        init_current(current)
+    }
+}
+
 /// Gets a handle to the thread that invokes it.
 ///
 /// # Examples
@@ -226,17 +243,17 @@ fn init_current(current: *mut ()) -> Thread {
         // a particular API should be entirely allocation-free, feel free to open
         // an issue on the Rust repository, we'll see what we can do.
         rtabort!(
-            "\n
-            Attempted to access thread-local data while allocating said data.\n
-            Do not access functions that allocate in the global allocator!\n
-            This is a bug in the global allocator.\n
-        "
+            "\n\
+            Attempted to access thread-local data while allocating said data.\n\
+            Do not access functions that allocate in the global allocator!\n\
+            This is a bug in the global allocator.\n\
+            "
         )
     } else {
         debug_assert_eq!(current, DESTROYED);
         panic!(
-            "use of std::thread::current() is not possible after the thread's
-         local data has been destroyed"
+            "use of std::thread::current() is not possible after the thread's \
+            local data has been destroyed"
         )
     }
 }
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 227ee9d64f3..2ff44fcd4c6 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -186,7 +186,12 @@ mod current;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use current::current;
-pub(crate) use current::{current_id, drop_current, set_current, try_current};
+pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current};
+
+mod spawnhook;
+
+#[unstable(feature = "thread_spawn_hook", issue = "132951")]
+pub use spawnhook::add_spawn_hook;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Thread-local storage
@@ -259,6 +264,8 @@ pub struct Builder {
     name: Option<String>,
     // The size of the stack for the spawned thread in bytes
     stack_size: Option<usize>,
+    // Skip running and inheriting the thread spawn hooks
+    no_hooks: bool,
 }
 
 impl Builder {
@@ -282,7 +289,7 @@ impl Builder {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn new() -> Builder {
-        Builder { name: None, stack_size: None }
+        Builder { name: None, stack_size: None, no_hooks: false }
     }
 
     /// Names the thread-to-be. Currently the name is used for identification
@@ -338,6 +345,16 @@ impl Builder {
         self
     }
 
+    /// Disables running and inheriting [spawn hooks](add_spawn_hook).
+    ///
+    /// Use this if the parent thread is in no way relevant for the child thread.
+    /// For example, when lazily spawning threads for a thread pool.
+    #[unstable(feature = "thread_spawn_hook", issue = "132951")]
+    pub fn no_hooks(mut self) -> Builder {
+        self.no_hooks = true;
+        self
+    }
+
     /// Spawns a new thread by taking ownership of the `Builder`, and returns an
     /// [`io::Result`] to its [`JoinHandle`].
     ///
@@ -460,7 +477,7 @@ impl Builder {
         F: Send,
         T: Send,
     {
-        let Builder { name, stack_size } = self;
+        let Builder { name, stack_size, no_hooks } = self;
 
         let stack_size = stack_size.unwrap_or_else(|| {
             static MIN: AtomicUsize = AtomicUsize::new(0);
@@ -485,6 +502,13 @@ impl Builder {
             Some(name) => Thread::new(id, name.into()),
             None => Thread::new_unnamed(id),
         };
+
+        let hooks = if no_hooks {
+            spawnhook::ChildSpawnHooks::default()
+        } else {
+            spawnhook::run_spawn_hooks(&my_thread)
+        };
+
         let their_thread = my_thread.clone();
 
         let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@@ -494,9 +518,6 @@ impl Builder {
         });
         let their_packet = my_packet.clone();
 
-        let output_capture = crate::io::set_output_capture(None);
-        crate::io::set_output_capture(output_capture.clone());
-
         // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*.
         // See <https://github.com/rust-lang/rust/issues/101983> for more details.
         // To prevent leaks we use a wrapper that drops its contents.
@@ -534,10 +555,9 @@ impl Builder {
                 imp::Thread::set_name(name);
             }
 
-            crate::io::set_output_capture(output_capture);
-
             let f = f.into_inner();
             let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+                crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run());
                 crate::sys::backtrace::__rust_begin_short_backtrace(f)
             }));
             // SAFETY: `their_packet` as been built just above and moved by the
@@ -1126,9 +1146,9 @@ pub fn park_timeout_ms(ms: u32) {
 #[stable(feature = "park_timeout", since = "1.4.0")]
 pub fn park_timeout(dur: Duration) {
     let guard = PanicGuard;
-    // SAFETY: park_timeout is called on the parker owned by this thread.
+    // SAFETY: park_timeout is called on a handle owned by this thread.
     unsafe {
-        current().0.parker().park_timeout(dur);
+        current().park_timeout(dur);
     }
     // No panic occurred, do not abort.
     forget(guard);
@@ -1426,6 +1446,15 @@ impl Thread {
         unsafe { self.0.parker().park() }
     }
 
+    /// Like the public [`park_timeout`], but callable on any handle. This is
+    /// used to allow parking in TLS destructors.
+    ///
+    /// # Safety
+    /// May only be called from the thread to which this handle belongs.
+    pub(crate) unsafe fn park_timeout(&self, dur: Duration) {
+        unsafe { self.0.parker().park_timeout(dur) }
+    }
+
     /// Atomically makes the handle's token available if it is not already.
     ///
     /// Every thread is equipped with some basic low-level blocking support, via
diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs
index b2305b1eda7..0033fc3a732 100644
--- a/library/std/src/thread/scoped.rs
+++ b/library/std/src/thread/scoped.rs
@@ -1,4 +1,4 @@
-use super::{Builder, JoinInner, Result, Thread, current, park};
+use super::{Builder, JoinInner, Result, Thread, current_or_unnamed};
 use crate::marker::PhantomData;
 use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
 use crate::sync::Arc;
@@ -140,7 +140,7 @@ where
     let scope = Scope {
         data: Arc::new(ScopeData {
             num_running_threads: AtomicUsize::new(0),
-            main_thread: current(),
+            main_thread: current_or_unnamed(),
             a_thread_panicked: AtomicBool::new(false),
         }),
         env: PhantomData,
@@ -152,7 +152,8 @@ where
 
     // Wait until all the threads are finished.
     while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
-        park();
+        // SAFETY: this is the main thread, the handle belongs to us.
+        unsafe { scope.data.main_thread.park() };
     }
 
     // Throw any panic from `f`, or the return value of `f` if no thread panicked.
@@ -176,7 +177,7 @@ impl<'scope, 'env> Scope<'scope, 'env> {
     /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
     /// the panic payload.
     ///
-    /// If the join handle is dropped, the spawned thread will implicitly joined at the
+    /// If the join handle is dropped, the spawned thread will be implicitly joined at the
     /// end of the scope. In that case, if the spawned thread panics, [`scope`] will
     /// panic after all threads are joined.
     ///
diff --git a/library/std/src/thread/spawnhook.rs b/library/std/src/thread/spawnhook.rs
new file mode 100644
index 00000000000..99b5ad9cb9f
--- /dev/null
+++ b/library/std/src/thread/spawnhook.rs
@@ -0,0 +1,148 @@
+use crate::cell::Cell;
+use crate::iter;
+use crate::sync::Arc;
+use crate::thread::Thread;
+
+crate::thread_local! {
+    /// A thread local linked list of spawn hooks.
+    ///
+    /// It is a linked list of Arcs, such that it can very cheaply be inhereted by spawned threads.
+    ///
+    /// (That technically makes it a set of linked lists with shared tails, so a linked tree.)
+    static SPAWN_HOOKS: Cell<SpawnHooks> = const { Cell::new(SpawnHooks { first: None }) };
+}
+
+#[derive(Default, Clone)]
+struct SpawnHooks {
+    first: Option<Arc<SpawnHook>>,
+}
+
+// Manually implement drop to prevent deep recursion when dropping linked Arc list.
+impl Drop for SpawnHooks {
+    fn drop(&mut self) {
+        let mut next = self.first.take();
+        while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) {
+            drop(hook);
+            next = n;
+        }
+    }
+}
+
+struct SpawnHook {
+    hook: Box<dyn Send + Sync + Fn(&Thread) -> Box<dyn Send + FnOnce()>>,
+    next: Option<Arc<SpawnHook>>,
+}
+
+/// Registers a function to run for every newly thread spawned.
+///
+/// The hook is executed in the parent thread, and returns a function
+/// that will be executed in the new thread.
+///
+/// The hook is called with the `Thread` handle for the new thread.
+///
+/// The hook will only be added for the current thread and is inherited by the threads it spawns.
+/// In other words, adding a hook has no effect on already running threads (other than the current
+/// thread) and the threads they might spawn in the future.
+///
+/// Hooks can only be added, not removed.
+///
+/// The hooks will run in reverse order, starting with the most recently added.
+///
+/// # Usage
+///
+/// ```
+/// #![feature(thread_spawn_hook)]
+///
+/// std::thread::add_spawn_hook(|_| {
+///     ..; // This will run in the parent (spawning) thread.
+///     move || {
+///         ..; // This will run it the child (spawned) thread.
+///     }
+/// });
+/// ```
+///
+/// # Example
+///
+/// A spawn hook can be used to "inherit" a thread local from the parent thread:
+///
+/// ```
+/// #![feature(thread_spawn_hook)]
+///
+/// use std::cell::Cell;
+///
+/// thread_local! {
+///     static X: Cell<u32> = Cell::new(0);
+/// }
+///
+/// // This needs to be done once in the main thread before spawning any threads.
+/// std::thread::add_spawn_hook(|_| {
+///     // Get the value of X in the spawning thread.
+///     let value = X.get();
+///     // Set the value of X in the newly spawned thread.
+///     move || X.set(value)
+/// });
+///
+/// X.set(123);
+///
+/// std::thread::spawn(|| {
+///     assert_eq!(X.get(), 123);
+/// }).join().unwrap();
+/// ```
+#[unstable(feature = "thread_spawn_hook", issue = "132951")]
+pub fn add_spawn_hook<F, G>(hook: F)
+where
+    F: 'static + Send + Sync + Fn(&Thread) -> G,
+    G: 'static + Send + FnOnce(),
+{
+    SPAWN_HOOKS.with(|h| {
+        let mut hooks = h.take();
+        let next = hooks.first.take();
+        hooks.first = Some(Arc::new(SpawnHook {
+            hook: Box::new(move |thread| Box::new(hook(thread))),
+            next,
+        }));
+        h.set(hooks);
+    });
+}
+
+/// Runs all the spawn hooks.
+///
+/// Called on the parent thread.
+///
+/// Returns the functions to be called on the newly spawned thread.
+pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks {
+    // Get a snapshot of the spawn hooks.
+    // (Increments the refcount to the first node.)
+    let hooks = SPAWN_HOOKS.with(|hooks| {
+        let snapshot = hooks.take();
+        hooks.set(snapshot.clone());
+        snapshot
+    });
+    // Iterate over the hooks, run them, and collect the results in a vector.
+    let to_run: Vec<_> = iter::successors(hooks.first.as_deref(), |hook| hook.next.as_deref())
+        .map(|hook| (hook.hook)(thread))
+        .collect();
+    // Pass on the snapshot of the hooks and the results to the new thread,
+    // which will then run SpawnHookResults::run().
+    ChildSpawnHooks { hooks, to_run }
+}
+
+/// The results of running the spawn hooks.
+///
+/// This struct is sent to the new thread.
+/// It contains the inherited hooks and the closures to be run.
+#[derive(Default)]
+pub(super) struct ChildSpawnHooks {
+    hooks: SpawnHooks,
+    to_run: Vec<Box<dyn FnOnce() + Send>>,
+}
+
+impl ChildSpawnHooks {
+    // This is run on the newly spawned thread, directly at the start.
+    pub(super) fn run(self) {
+        SPAWN_HOOKS.set(self.hooks);
+        for run in self.to_run {
+            run();
+        }
+    }
+}