about summary refs log tree commit diff
path: root/library/std
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-01-19 17:32:20 +0000
committerGitHub <noreply@github.com>2025-01-19 17:32:20 +0000
commit49375c48f7748debfdbfab9cccc450235e72bb74 (patch)
tree9cf2eda04ec28b2451ab0770098627aa6bcc5134 /library/std
parent5460fbe610cacae6de98a3dfc3ffe9573f0c9906 (diff)
parent544695506c1d85628d39856ae912cbfeb112021d (diff)
Merge pull request #4141 from rust-lang/rustup-2025-01-19
Automatic Rustup
Diffstat (limited to 'library/std')
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/io/mod.rs249
-rw-r--r--library/std/src/io/tests.rs17
-rw-r--r--library/std/src/lib.rs3
-rw-r--r--library/std/src/panicking.rs39
-rw-r--r--library/std/src/pipe.rs258
-rw-r--r--library/std/src/pipe/tests.rs19
-rw-r--r--library/std/src/rt.rs23
-rw-r--r--library/std/src/sync/lazy_lock.rs2
-rw-r--r--library/std/src/sys/anonymous_pipe/unix.rs3
-rw-r--r--library/std/src/sys/anonymous_pipe/unsupported.rs3
-rw-r--r--library/std/src/sys/anonymous_pipe/windows.rs4
-rw-r--r--library/std/src/sys/pal/unix/fs.rs36
-rw-r--r--library/std/src/sys/pal/unix/kernel_copy.rs5
-rw-r--r--library/std/src/sys/pal/unix/stack_overflow.rs9
-rw-r--r--library/std/src/sys/pal/windows/stack_overflow.rs8
-rw-r--r--library/std/src/thread/current.rs39
-rw-r--r--library/std/src/thread/mod.rs276
-rw-r--r--library/std/tests/pipe_subprocess.rs3
19 files changed, 486 insertions, 512 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index e7f7f38cb41..da58d7c13bd 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
 panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core", public = true }
-compiler_builtins = { version = "=0.1.140" }
+compiler_builtins = { version = "=0.1.143" }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.15", default-features = false, features = [
     'rustc-dep-of-std',
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 7912f969bbd..231c8712ebd 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -330,6 +330,7 @@ pub use self::{
 };
 use crate::mem::take;
 use crate::ops::{Deref, DerefMut};
+use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
 use crate::{cmp, fmt, slice, str, sys};
 
 mod buffered;
@@ -3250,3 +3251,251 @@ impl<B: BufRead> Iterator for Lines<B> {
         }
     }
 }
+
+/// Create anonymous pipe that is close-on-exec and blocking.
+///
+/// # Behavior
+///
+/// A pipe is a synchronous, unidirectional data channel between two or more processes, like an
+/// interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
+///
+/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
+/// * A write on a [`PipeWriter`] blocks when the pipe is full.
+/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
+///   returns EOF.
+/// * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
+///
+/// # Capacity
+///
+/// Pipe capacity is platform dependent. To quote the Linux [man page]:
+///
+/// > Different implementations have different limits for the pipe capacity. Applications should
+/// > not rely on a particular capacity: an application should be designed so that a reading process
+/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
+///
+/// # Examples
+///
+/// ```no_run
+/// #![feature(anonymous_pipe)]
+/// # #[cfg(miri)] fn main() {}
+/// # #[cfg(not(miri))]
+/// # fn main() -> std::io::Result<()> {
+/// # use std::process::Command;
+/// # use std::io::{Read, Write};
+/// let (ping_rx, mut ping_tx) = std::io::pipe()?;
+/// let (mut pong_rx, pong_tx) = std::io::pipe()?;
+///
+/// // Spawn a process that echoes its input.
+/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
+///
+/// ping_tx.write_all(b"hello")?;
+/// // Close to unblock echo_server's reader.
+/// drop(ping_tx);
+///
+/// let mut buf = String::new();
+/// // Block until echo_server's writer is closed.
+/// pong_rx.read_to_string(&mut buf)?;
+/// assert_eq!(&buf, "hello");
+///
+/// echo_server.wait()?;
+/// # Ok(())
+/// # }
+/// ```
+/// [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
+/// [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
+/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[inline]
+pub fn pipe() -> Result<(PipeReader, PipeWriter)> {
+    pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
+}
+
+/// Read end of the anonymous pipe.
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[derive(Debug)]
+pub struct PipeReader(pub(crate) AnonPipe);
+
+/// Write end of the anonymous pipe.
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+#[derive(Debug)]
+pub struct PipeWriter(pub(crate) AnonPipe);
+
+impl PipeReader {
+    /// Create a new [`PipeReader`] instance that shares the same underlying file description.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(anonymous_pipe)]
+    /// # #[cfg(miri)] fn main() {}
+    /// # #[cfg(not(miri))]
+    /// # fn main() -> std::io::Result<()> {
+    /// # use std::fs;
+    /// # use std::io::Write;
+    /// # use std::process::Command;
+    /// const NUM_SLOT: u8 = 2;
+    /// const NUM_PROC: u8 = 5;
+    /// const OUTPUT: &str = "work.txt";
+    ///
+    /// let mut jobs = vec![];
+    /// let (reader, mut writer) = std::io::pipe()?;
+    ///
+    /// // Write NUM_SLOT characters the pipe.
+    /// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
+    ///
+    /// // Spawn several processes that read a character from the pipe, do some work, then
+    /// // write back to the pipe. When the pipe is empty, the processes block, so only
+    /// // NUM_SLOT processes can be working at any given time.
+    /// for _ in 0..NUM_PROC {
+    ///     jobs.push(
+    ///         Command::new("bash")
+    ///             .args(["-c",
+    ///                 &format!(
+    ///                      "read -n 1\n\
+    ///                       echo -n 'x' >> '{OUTPUT}'\n\
+    ///                       echo -n '|'",
+    ///                 ),
+    ///             ])
+    ///             .stdin(reader.try_clone()?)
+    ///             .stdout(writer.try_clone()?)
+    ///             .spawn()?,
+    ///     );
+    /// }
+    ///
+    /// // Wait for all jobs to finish.
+    /// for mut job in jobs {
+    ///     job.wait()?;
+    /// }
+    ///
+    /// // Check our work and clean up.
+    /// let xs = fs::read_to_string(OUTPUT)?;
+    /// fs::remove_file(OUTPUT)?;
+    /// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "anonymous_pipe", issue = "127154")]
+    pub fn try_clone(&self) -> Result<Self> {
+        self.0.try_clone().map(Self)
+    }
+}
+
+impl PipeWriter {
+    /// Create a new [`PipeWriter`] instance that shares the same underlying file description.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(anonymous_pipe)]
+    /// # #[cfg(miri)] fn main() {}
+    /// # #[cfg(not(miri))]
+    /// # fn main() -> std::io::Result<()> {
+    /// # use std::process::Command;
+    /// # use std::io::Read;
+    /// let (mut reader, writer) = std::io::pipe()?;
+    ///
+    /// // Spawn a process that writes to stdout and stderr.
+    /// let mut peer = Command::new("bash")
+    ///     .args([
+    ///         "-c",
+    ///         "echo -n foo\n\
+    ///          echo -n bar >&2"
+    ///     ])
+    ///     .stdout(writer.try_clone()?)
+    ///     .stderr(writer)
+    ///     .spawn()?;
+    ///
+    /// // Read and check the result.
+    /// let mut msg = String::new();
+    /// reader.read_to_string(&mut msg)?;
+    /// assert_eq!(&msg, "foobar");
+    ///
+    /// peer.wait()?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "anonymous_pipe", issue = "127154")]
+    pub fn try_clone(&self) -> Result<Self> {
+        self.0.try_clone().map(Self)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Read for &PipeReader {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        self.0.read(buf)
+    }
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+        self.0.read_to_end(buf)
+    }
+    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
+        self.0.read_buf(buf)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Read for PipeReader {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        self.0.read(buf)
+    }
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+        self.0.read_to_end(buf)
+    }
+    fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
+        self.0.read_buf(buf)
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Write for &PipeWriter {
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        self.0.write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+}
+
+#[unstable(feature = "anonymous_pipe", issue = "127154")]
+impl Write for PipeWriter {
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        self.0.write(buf)
+    }
+    #[inline]
+    fn flush(&mut self) -> Result<()> {
+        Ok(())
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+}
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index 47cbb9614af..85098b3bb18 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -823,3 +823,20 @@ fn try_oom_error() {
     let io_err = io::Error::from(reserve_err);
     assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
 }
+
+#[test]
+#[cfg(all(windows, unix, not(miri)))]
+fn pipe_creation_clone_and_rw() {
+    let (rx, tx) = std::io::pipe().unwrap();
+
+    tx.try_clone().unwrap().write_all(b"12345").unwrap();
+    drop(tx);
+
+    let mut rx2 = rx.try_clone().unwrap();
+    drop(rx);
+
+    let mut s = String::new();
+    rx2.read_to_string(&mut s).unwrap();
+    drop(rx2);
+    assert_eq!(s, "12345");
+}
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 5c12236617c..39f234e4ba6 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -333,7 +333,6 @@
 #![feature(extend_one)]
 #![feature(float_gamma)]
 #![feature(float_minimum_maximum)]
-#![feature(float_next_up_down)]
 #![feature(fmt_internals)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
@@ -596,8 +595,6 @@ pub mod panic;
 #[unstable(feature = "pattern_type_macro", issue = "123646")]
 pub mod pat;
 pub mod path;
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-pub mod pipe;
 pub mod process;
 #[unstable(feature = "random", issue = "130703")]
 pub mod random;
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 3b02254548b..8e50bf11dd0 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -258,31 +258,34 @@ fn default_hook(info: &PanicHookInfo<'_>) {
     let location = info.location().unwrap();
 
     let msg = payload_as_str(info.payload());
-    let thread = thread::try_current();
-    let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
 
     let write = #[optimize(size)]
     |err: &mut dyn crate::io::Write| {
         // Use a lock to prevent mixed output in multithreading context.
         // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows.
         let mut lock = backtrace::lock();
-        // Try to write the panic message to a buffer first to prevent other concurrent outputs
-        // interleaving with it.
-        let mut buffer = [0u8; 512];
-        let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
-
-        let write_msg = |dst: &mut dyn crate::io::Write| {
-            // We add a newline to ensure the panic message appears at the start of a line.
-            writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}")
-        };
 
-        if write_msg(&mut cursor).is_ok() {
-            let pos = cursor.position() as usize;
-            let _ = err.write_all(&buffer[0..pos]);
-        } else {
-            // The message did not fit into the buffer, write it directly instead.
-            let _ = write_msg(err);
-        };
+        thread::with_current_name(|name| {
+            let name = name.unwrap_or("<unnamed>");
+
+            // Try to write the panic message to a buffer first to prevent other concurrent outputs
+            // interleaving with it.
+            let mut buffer = [0u8; 512];
+            let mut cursor = crate::io::Cursor::new(&mut buffer[..]);
+
+            let write_msg = |dst: &mut dyn crate::io::Write| {
+                // We add a newline to ensure the panic message appears at the start of a line.
+                writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}")
+            };
+
+            if write_msg(&mut cursor).is_ok() {
+                let pos = cursor.position() as usize;
+                let _ = err.write_all(&buffer[0..pos]);
+            } else {
+                // The message did not fit into the buffer, write it directly instead.
+                let _ = write_msg(err);
+            };
+        });
 
         static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
 
diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs
deleted file mode 100644
index 913c22588a7..00000000000
--- a/library/std/src/pipe.rs
+++ /dev/null
@@ -1,258 +0,0 @@
-//!  A cross-platform anonymous pipe.
-//!
-//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on
-//! Windows.
-//!
-//! # Behavior
-//!
-//! A pipe is a synchronous, unidirectional data channel between two or more processes, like an
-//! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
-//!
-//! * A read on a [`PipeReader`] blocks until the pipe is non-empty.
-//! * A write on a [`PipeWriter`] blocks when the pipe is full.
-//! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
-//!   returns EOF.
-//! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
-//!
-//! # Capacity
-//!
-//! Pipe capacity is platform dependent. To quote the Linux [man page]:
-//!
-//! > Different implementations have different limits for the pipe capacity. Applications should
-//! > not rely on a particular capacity: an application should be designed so that a reading process
-//! > consumes data as soon as it is available, so that a writing process does not remain blocked.
-//!
-//! # Examples
-//!
-//! ```no_run
-//! #![feature(anonymous_pipe)]
-//! # #[cfg(miri)] fn main() {}
-//! # #[cfg(not(miri))]
-//! # fn main() -> std::io::Result<()> {
-//! # use std::process::Command;
-//! # use std::io::{Read, Write};
-//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?;
-//! let (mut pong_rx, pong_tx) = std::pipe::pipe()?;
-//!
-//! // Spawn a process that echoes its input.
-//! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
-//!
-//! ping_tx.write_all(b"hello")?;
-//! // Close to unblock echo_server's reader.
-//! drop(ping_tx);
-//!
-//! let mut buf = String::new();
-//! // Block until echo_server's writer is closed.
-//! pong_rx.read_to_string(&mut buf)?;
-//! assert_eq!(&buf, "hello");
-//!
-//! echo_server.wait()?;
-//! # Ok(())
-//! # }
-//! ```
-//! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
-//! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
-//! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
-use crate::io;
-use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
-
-/// Create anonymous pipe that is close-on-exec and blocking.
-///
-/// # Examples
-///
-/// See the [module-level](crate::pipe) documentation for examples.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[inline]
-pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
-    pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
-}
-
-/// Read end of the anonymous pipe.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[derive(Debug)]
-pub struct PipeReader(pub(crate) AnonPipe);
-
-/// Write end of the anonymous pipe.
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-#[derive(Debug)]
-pub struct PipeWriter(pub(crate) AnonPipe);
-
-impl PipeReader {
-    /// Create a new [`PipeReader`] instance that shares the same underlying file description.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(anonymous_pipe)]
-    /// # #[cfg(miri)] fn main() {}
-    /// # #[cfg(not(miri))]
-    /// # fn main() -> std::io::Result<()> {
-    /// # use std::fs;
-    /// # use std::io::Write;
-    /// # use std::process::Command;
-    /// const NUM_SLOT: u8 = 2;
-    /// const NUM_PROC: u8 = 5;
-    /// const OUTPUT: &str = "work.txt";
-    ///
-    /// let mut jobs = vec![];
-    /// let (reader, mut writer) = std::pipe::pipe()?;
-    ///
-    /// // Write NUM_SLOT characters the pipe.
-    /// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
-    ///
-    /// // Spawn several processes that read a character from the pipe, do some work, then
-    /// // write back to the pipe. When the pipe is empty, the processes block, so only
-    /// // NUM_SLOT processes can be working at any given time.
-    /// for _ in 0..NUM_PROC {
-    ///     jobs.push(
-    ///         Command::new("bash")
-    ///             .args(["-c",
-    ///                 &format!(
-    ///                      "read -n 1\n\
-    ///                       echo -n 'x' >> '{OUTPUT}'\n\
-    ///                       echo -n '|'",
-    ///                 ),
-    ///             ])
-    ///             .stdin(reader.try_clone()?)
-    ///             .stdout(writer.try_clone()?)
-    ///             .spawn()?,
-    ///     );
-    /// }
-    ///
-    /// // Wait for all jobs to finish.
-    /// for mut job in jobs {
-    ///     job.wait()?;
-    /// }
-    ///
-    /// // Check our work and clean up.
-    /// let xs = fs::read_to_string(OUTPUT)?;
-    /// fs::remove_file(OUTPUT)?;
-    /// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "anonymous_pipe", issue = "127154")]
-    pub fn try_clone(&self) -> io::Result<Self> {
-        self.0.try_clone().map(Self)
-    }
-}
-
-impl PipeWriter {
-    /// Create a new [`PipeWriter`] instance that shares the same underlying file description.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(anonymous_pipe)]
-    /// # #[cfg(miri)] fn main() {}
-    /// # #[cfg(not(miri))]
-    /// # fn main() -> std::io::Result<()> {
-    /// # use std::process::Command;
-    /// # use std::io::Read;
-    /// let (mut reader, writer) = std::pipe::pipe()?;
-    ///
-    /// // Spawn a process that writes to stdout and stderr.
-    /// let mut peer = Command::new("bash")
-    ///     .args([
-    ///         "-c",
-    ///         "echo -n foo\n\
-    ///          echo -n bar >&2"
-    ///     ])
-    ///     .stdout(writer.try_clone()?)
-    ///     .stderr(writer)
-    ///     .spawn()?;
-    ///
-    /// // Read and check the result.
-    /// let mut msg = String::new();
-    /// reader.read_to_string(&mut msg)?;
-    /// assert_eq!(&msg, "foobar");
-    ///
-    /// peer.wait()?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "anonymous_pipe", issue = "127154")]
-    pub fn try_clone(&self) -> io::Result<Self> {
-        self.0.try_clone().map(Self)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Read for &PipeReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
-    }
-    fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
-        self.0.read_vectored(bufs)
-    }
-    #[inline]
-    fn is_read_vectored(&self) -> bool {
-        self.0.is_read_vectored()
-    }
-    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
-        self.0.read_to_end(buf)
-    }
-    fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
-        self.0.read_buf(buf)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Read for PipeReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
-    }
-    fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
-        self.0.read_vectored(bufs)
-    }
-    #[inline]
-    fn is_read_vectored(&self) -> bool {
-        self.0.is_read_vectored()
-    }
-    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
-        self.0.read_to_end(buf)
-    }
-    fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
-        self.0.read_buf(buf)
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Write for &PipeWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-
-    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
-        self.0.write_vectored(bufs)
-    }
-
-    #[inline]
-    fn is_write_vectored(&self) -> bool {
-        self.0.is_write_vectored()
-    }
-}
-
-#[unstable(feature = "anonymous_pipe", issue = "127154")]
-impl io::Write for PipeWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
-    }
-    #[inline]
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-
-    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
-        self.0.write_vectored(bufs)
-    }
-
-    #[inline]
-    fn is_write_vectored(&self) -> bool {
-        self.0.is_write_vectored()
-    }
-}
diff --git a/library/std/src/pipe/tests.rs b/library/std/src/pipe/tests.rs
deleted file mode 100644
index 9c38e106787..00000000000
--- a/library/std/src/pipe/tests.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use crate::io::{Read, Write};
-use crate::pipe::pipe;
-
-#[test]
-#[cfg(all(windows, unix, not(miri)))]
-fn pipe_creation_clone_and_rw() {
-    let (rx, tx) = pipe().unwrap();
-
-    tx.try_clone().unwrap().write_all(b"12345").unwrap();
-    drop(tx);
-
-    let mut rx2 = rx.try_clone().unwrap();
-    drop(rx);
-
-    let mut s = String::new();
-    rx2.read_to_string(&mut s).unwrap();
-    drop(rx2);
-    assert_eq!(s, "12345");
-}
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index 24a362072ab..384369b0012 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt};
 #[rustfmt::skip]
 use crate::any::Any;
 use crate::sync::Once;
-use crate::thread::{self, Thread};
+use crate::thread::{self, main_thread};
 use crate::{mem, panic, sys};
 
 // Prints to the "panic output", depending on the platform this may be:
@@ -102,24 +102,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
         sys::init(argc, argv, sigpipe)
     };
 
-    // Set up the current thread handle to give it the right name.
-    //
-    // When code running before main uses `ReentrantLock` (for example by
-    // using `println!`), the thread ID can become initialized before we
-    // create this handle. Since `set_current` fails when the ID of the
-    // handle does not match the current ID, we should attempt to use the
-    // current thread ID here instead of unconditionally creating a new
-    // one. Also see #130210.
-    let thread = unsafe { Thread::new_main(thread::current_id()) };
-    if let Err(_thread) = thread::set_current(thread) {
-        // `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
-        // bad idea though, as we then cannot set the main thread name here.
-        //
-        // FIXME: detect the main thread in `thread::current` and use the
-        //        correct name there.
-        rtabort!("code running before main must not use thread::current");
-    }
+    // Remember the main thread ID to give it the correct name.
+    // SAFETY: this is the only time and place where we call this function.
+    unsafe { main_thread::set(thread::current_id()) };
 }
 
 /// Clean up the thread-local runtime state. This *should* be run after all other
diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs
index 1e4f9b79e0f..98c83d8d326 100644
--- a/library/std/src/sync/lazy_lock.rs
+++ b/library/std/src/sync/lazy_lock.rs
@@ -31,7 +31,7 @@ union Data<T, F> {
 /// ```
 /// use std::sync::LazyLock;
 ///
-/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated.
+/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated.
 /// // this is fine, as the OS can deallocate the terminated program faster than we can free memory
 /// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional.
 /// static DEEP_THOUGHT: LazyLock<String> = LazyLock::new(|| {
diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs
index 9168024730e..9e398765634 100644
--- a/library/std/src/sys/anonymous_pipe/unix.rs
+++ b/library/std/src/sys/anonymous_pipe/unix.rs
@@ -1,6 +1,5 @@
-use crate::io;
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::Stdio;
 use crate::sys::fd::FileDesc;
 use crate::sys::pipe::anon_pipe;
diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs
index dd51e70315e..4e79ac9c21a 100644
--- a/library/std/src/sys/anonymous_pipe/unsupported.rs
+++ b/library/std/src/sys/anonymous_pipe/unsupported.rs
@@ -1,5 +1,4 @@
-use crate::io;
-use crate::pipe::{PipeReader, PipeWriter};
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::process::Stdio;
 pub use crate::sys::pipe::AnonPipe;
 
diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs
index a48198f8a81..eb7fa8ec1c9 100644
--- a/library/std/src/sys/anonymous_pipe/windows.rs
+++ b/library/std/src/sys/anonymous_pipe/windows.rs
@@ -1,12 +1,12 @@
+use crate::io::{self, PipeReader, PipeWriter};
 use crate::os::windows::io::{
     AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
 };
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::Stdio;
+use crate::ptr;
 use crate::sys::c;
 use crate::sys::handle::Handle;
 use crate::sys_common::{FromInner, IntoInner};
-use crate::{io, ptr};
 
 pub type AnonPipe = Handle;
 
diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs
index 54fa31620ac..fdf011c1948 100644
--- a/library/std/src/sys/pal/unix/fs.rs
+++ b/library/std/src/sys/pal/unix/fs.rs
@@ -709,7 +709,7 @@ impl Iterator for ReadDir {
                 // thread safety for readdir() as long an individual DIR* is not accessed
                 // concurrently, which is sufficient for Rust.
                 super::os::set_errno(0);
-                let entry_ptr = readdir64(self.inner.dirp.0);
+                let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0);
                 if entry_ptr.is_null() {
                     // We either encountered an error, or reached the end. Either way,
                     // the next call to next() should return None.
@@ -735,29 +735,19 @@ impl Iterator for ReadDir {
                 // contents were "simply" partially initialized data.
                 //
                 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
-                // would not be legal. However, unique to dirent64 is that we don't even
-                // get to use `&raw const (*entry_ptr).d_name` because that operation
-                // requires the full extent of *entry_ptr to be in bounds of the same
-                // allocation, which is not necessarily the case here.
-                //
-                // Instead we must access fields individually through their offsets.
-                macro_rules! offset_ptr {
-                    ($entry_ptr:expr, $field:ident) => {{
-                        const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize;
-                        if true {
-                            // Cast to the same type determined by the else branch.
-                            $entry_ptr.byte_offset(OFFSET).cast::<_>()
-                        } else {
-                            #[allow(deref_nullptr)]
-                            {
-                                &raw const (*ptr::null::<dirent64>()).$field
-                            }
-                        }
-                    }};
+                // would not be legal. However, we can use `&raw const (*entry_ptr).d_name`
+                // to refer the fields individually, because that operation is equivalent
+                // to `byte_offset` and thus does not require the full extent of `*entry_ptr`
+                // to be in bounds of the same allocation, only the offset of the field
+                // being referenced.
+                macro_rules! entry_field_ptr {
+                    ($field:ident) => {
+                        &raw const (*entry_ptr).$field
+                    };
                 }
 
                 // d_name is guaranteed to be null-terminated.
-                let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast());
+                let name = CStr::from_ptr(entry_field_ptr!(d_name).cast());
                 let name_bytes = name.to_bytes();
                 if name_bytes == b"." || name_bytes == b".." {
                     continue;
@@ -765,14 +755,14 @@ impl Iterator for ReadDir {
 
                 #[cfg(not(target_os = "vita"))]
                 let entry = dirent64_min {
-                    d_ino: *offset_ptr!(entry_ptr, d_ino) as u64,
+                    d_ino: *entry_field_ptr!(d_ino) as u64,
                     #[cfg(not(any(
                         target_os = "solaris",
                         target_os = "illumos",
                         target_os = "aix",
                         target_os = "nto",
                     )))]
-                    d_type: *offset_ptr!(entry_ptr, d_type) as u8,
+                    d_type: *entry_field_ptr!(d_type) as u8,
                 };
 
                 #[cfg(target_os = "vita")]
diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs
index 36823a503b1..bbf29f32523 100644
--- a/library/std/src/sys/pal/unix/kernel_copy.rs
+++ b/library/std/src/sys/pal/unix/kernel_copy.rs
@@ -52,15 +52,14 @@ use crate::cmp::min;
 use crate::fs::{File, Metadata};
 use crate::io::copy::generic_copy;
 use crate::io::{
-    BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
-    Write,
+    BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock,
+    StdinLock, StdoutLock, Take, Write,
 };
 use crate::mem::ManuallyDrop;
 use crate::net::TcpStream;
 use crate::os::unix::fs::FileTypeExt;
 use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use crate::os::unix::net::UnixStream;
-use crate::pipe::{PipeReader, PipeWriter};
 use crate::process::{ChildStderr, ChildStdin, ChildStdout};
 use crate::ptr;
 use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs
index 69b31da427f..db5c6bd3a1c 100644
--- a/library/std/src/sys/pal/unix/stack_overflow.rs
+++ b/library/std/src/sys/pal/unix/stack_overflow.rs
@@ -100,10 +100,11 @@ mod imp {
         // If the faulting address is within the guard page, then we print a
         // message saying so and abort.
         if start <= addr && addr < end {
-            rtprintpanic!(
-                "\nthread '{}' has overflowed its stack\n",
-                thread::current().name().unwrap_or("<unknown>")
-            );
+            thread::with_current_name(|name| {
+                let name = name.unwrap_or("<unknown>");
+                rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
+            });
+
             rtabort!("stack overflow");
         } else {
             // Unregister ourselves by reverting back to the default behavior.
diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs
index 467e21ab56a..734cd30bed0 100644
--- a/library/std/src/sys/pal/windows/stack_overflow.rs
+++ b/library/std/src/sys/pal/windows/stack_overflow.rs
@@ -18,10 +18,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN
         let code = rec.ExceptionCode;
 
         if code == c::EXCEPTION_STACK_OVERFLOW {
-            rtprintpanic!(
-                "\nthread '{}' has overflowed its stack\n",
-                thread::current().name().unwrap_or("<unknown>")
-            );
+            thread::with_current_name(|name| {
+                let name = name.unwrap_or("<unknown>");
+                rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
+            });
         }
         c::EXCEPTION_CONTINUE_SEARCH
     }
diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs
index 3d2c288b360..414711298f0 100644
--- a/library/std/src/thread/current.rs
+++ b/library/std/src/thread/current.rs
@@ -15,7 +15,7 @@ local_pointer! {
 ///
 /// We store the thread ID so that it never gets destroyed during the lifetime
 /// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s.
-mod id {
+pub(super) mod id {
     use super::*;
 
     cfg_if::cfg_if! {
@@ -27,7 +27,7 @@ mod id {
 
             pub(super) const CHEAP: bool = true;
 
-            pub(super) fn get() -> Option<ThreadId> {
+            pub(crate) fn get() -> Option<ThreadId> {
                 ID.get()
             }
 
@@ -44,7 +44,7 @@ mod id {
 
             pub(super) const CHEAP: bool = false;
 
-            pub(super) fn get() -> Option<ThreadId> {
+            pub(crate) fn get() -> Option<ThreadId> {
                 let id0 = ID0.get().addr() as u64;
                 let id16 = ID16.get().addr() as u64;
                 let id32 = ID32.get().addr() as u64;
@@ -67,7 +67,7 @@ mod id {
 
             pub(super) const CHEAP: bool = false;
 
-            pub(super) fn get() -> Option<ThreadId> {
+            pub(crate) fn get() -> Option<ThreadId> {
                 let id0 = ID0.get().addr() as u64;
                 let id32 = ID32.get().addr() as u64;
                 ThreadId::from_u64((id32 << 32) + id0)
@@ -85,7 +85,7 @@ mod id {
 
             pub(super) const CHEAP: bool = true;
 
-            pub(super) fn get() -> Option<ThreadId> {
+            pub(crate) fn get() -> Option<ThreadId> {
                 let id = ID.get().addr() as u64;
                 ThreadId::from_u64(id)
             }
@@ -112,7 +112,7 @@ mod id {
 
 /// Tries to set the thread handle for the current thread. Fails if a handle was
 /// already set or if the thread ID of `thread` would change an already-set ID.
-pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> {
+pub(super) fn set_current(thread: Thread) -> Result<(), Thread> {
     if CURRENT.get() != NONE {
         return Err(thread);
     }
@@ -140,28 +140,31 @@ pub(crate) fn current_id() -> ThreadId {
     // to retrieve it from the current thread handle, which will only take one
     // TLS access.
     if !id::CHEAP {
-        let current = CURRENT.get();
-        if current > DESTROYED {
-            unsafe {
-                let current = ManuallyDrop::new(Thread::from_raw(current));
-                return current.id();
-            }
+        if let Some(id) = try_with_current(|t| t.map(|t| t.id())) {
+            return id;
         }
     }
 
     id::get_or_init()
 }
 
-/// Gets a handle to the thread that invokes it, if the handle has been initialized.
-pub(crate) fn try_current() -> Option<Thread> {
+/// Gets a reference to the handle of the thread that invokes it, if the handle
+/// has been initialized.
+pub(super) fn try_with_current<F, R>(f: F) -> R
+where
+    F: FnOnce(Option<&Thread>) -> R,
+{
     let current = CURRENT.get();
     if current > DESTROYED {
+        // SAFETY: `Arc` does not contain interior mutability, so it does not
+        // matter that the address of the handle might be different depending
+        // on where this is called.
         unsafe {
             let current = ManuallyDrop::new(Thread::from_raw(current));
-            Some((*current).clone())
+            f(Some(&current))
         }
     } else {
-        None
+        f(None)
     }
 }
 
@@ -176,7 +179,7 @@ pub(crate) fn current_or_unnamed() -> Thread {
             (*current).clone()
         }
     } else if current == DESTROYED {
-        Thread::new_unnamed(id::get_or_init())
+        Thread::new(id::get_or_init(), None)
     } else {
         init_current(current)
     }
@@ -221,7 +224,7 @@ fn init_current(current: *mut ()) -> Thread {
         CURRENT.set(BUSY);
         // If the thread ID was initialized already, use it.
         let id = id::get_or_init();
-        let thread = Thread::new_unnamed(id);
+        let thread = Thread::new(id, None);
 
         // Make sure that `crate::rt::thread_cleanup` will be run, which will
         // call `drop_current`.
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index b6ee00a253a..59b395336f2 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -158,12 +158,9 @@
 #[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
 mod tests;
 
-use core::cell::SyncUnsafeCell;
-use core::ffi::CStr;
-use core::mem::MaybeUninit;
-
 use crate::any::Any;
 use crate::cell::UnsafeCell;
+use crate::ffi::CStr;
 use crate::marker::PhantomData;
 use crate::mem::{self, ManuallyDrop, forget};
 use crate::num::NonZero;
@@ -186,7 +183,8 @@ mod current;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use current::current;
-pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current};
+pub(crate) use current::{current_id, current_or_unnamed, drop_current};
+use current::{set_current, try_with_current};
 
 mod spawnhook;
 
@@ -501,10 +499,7 @@ impl Builder {
         });
 
         let id = ThreadId::new();
-        let my_thread = match name {
-            Some(name) => Thread::new(id, name),
-            None => Thread::new_unnamed(id),
-        };
+        let my_thread = Thread::new(id, name);
 
         let hooks = if no_hooks {
             spawnhook::ChildSpawnHooks::default()
@@ -1235,7 +1230,7 @@ impl ThreadId {
         }
     }
 
-    #[cfg(not(target_thread_local))]
+    #[cfg(any(not(target_thread_local), target_has_atomic = "64"))]
     fn from_u64(v: u64) -> Option<ThreadId> {
         NonZero::new(v).map(ThreadId)
     }
@@ -1261,29 +1256,14 @@ impl ThreadId {
 
 // This module ensures private fields are kept private, which is necessary to enforce the safety requirements.
 mod thread_name_string {
-    use core::str;
-
     use crate::ffi::{CStr, CString};
+    use crate::str;
 
     /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated.
     pub(crate) struct ThreadNameString {
         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 {
-        type Target = CStr;
-        fn deref(&self) -> &CStr {
-            &self.inner
-        }
-    }
-
     impl From<String> for ThreadNameString {
         fn from(s: String) -> Self {
             Self {
@@ -1291,82 +1271,124 @@ mod thread_name_string {
             }
         }
     }
-}
-pub(crate) use thread_name_string::ThreadNameString;
-
-static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit<ThreadId>, MaybeUninit<Parker>)> =
-    SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit()));
-
-/// The internal representation of a `Thread` that is not the main thread.
-struct OtherInner {
-    name: Option<ThreadNameString>,
-    id: ThreadId,
-    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 {
-    fn id(&self) -> ThreadId {
-        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(),
+    impl ThreadNameString {
+        pub fn as_cstr(&self) -> &CStr {
+            &self.inner
         }
-    }
 
-    fn name(&self) -> Option<&str> {
-        match self {
-            Self::Main(_) => Some("main"),
-            Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str),
+        pub fn as_str(&self) -> &str {
+            // SAFETY: `ThreadNameString` is guaranteed to be UTF-8.
+            unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) }
         }
     }
+}
 
-    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 ()
+use thread_name_string::ThreadNameString;
+
+/// Store the ID of the main thread.
+///
+/// The thread handle for the main thread is created lazily, and this might even
+/// happen pre-main. Since not every platform has a way to identify the main
+/// thread when that happens – macOS's `pthread_main_np` function being a notable
+/// exception – we cannot assign it the right name right then. Instead, in our
+/// runtime startup code, we remember the thread ID of the main thread (through
+/// this modules `set` function) and use it to identify the main thread from then
+/// on. This works reliably and has the additional advantage that we can report
+/// the right thread name on main even after the thread handle has been destroyed.
+/// Note however that this also means that the name reported in pre-main functions
+/// will be incorrect, but that's just something we have to live with.
+pub(crate) mod main_thread {
+    cfg_if::cfg_if! {
+        if #[cfg(target_has_atomic = "64")] {
+            use super::ThreadId;
+            use crate::sync::atomic::AtomicU64;
+            use crate::sync::atomic::Ordering::Relaxed;
+
+            static MAIN: AtomicU64 = AtomicU64::new(0);
+
+            pub(super) fn get() -> Option<ThreadId> {
+                ThreadId::from_u64(MAIN.load(Relaxed))
             }
-        }
-    }
 
-    /// # 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() })
+            /// # Safety
+            /// May only be called once.
+            pub(crate) unsafe fn set(id: ThreadId) {
+                MAIN.store(id.as_u64().get(), Relaxed)
+            }
         } else {
-            // Safety: Upheld by caller
-            Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) })
+            use super::ThreadId;
+            use crate::mem::MaybeUninit;
+            use crate::sync::atomic::AtomicBool;
+            use crate::sync::atomic::Ordering::{Acquire, Release};
+
+            static INIT: AtomicBool = AtomicBool::new(false);
+            static mut MAIN: MaybeUninit<ThreadId> = MaybeUninit::uninit();
+
+            pub(super) fn get() -> Option<ThreadId> {
+                if INIT.load(Acquire) {
+                    Some(unsafe { MAIN.assume_init() })
+                } else {
+                    None
+                }
+            }
+
+            /// # Safety
+            /// May only be called once.
+            pub(crate) unsafe fn set(id: ThreadId) {
+                unsafe { MAIN = MaybeUninit::new(id) };
+                INIT.store(true, Release);
+            }
         }
     }
+}
 
-    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)
-            },
+/// Run a function with the current thread's name.
+///
+/// Modulo thread local accesses, this function is safe to call from signal
+/// handlers and in similar circumstances where allocations are not possible.
+pub(crate) fn with_current_name<F, R>(f: F) -> R
+where
+    F: FnOnce(Option<&str>) -> R,
+{
+    try_with_current(|thread| {
+        if let Some(thread) = thread {
+            // If there is a current thread handle, try to use the name stored
+            // there.
+            if let Some(name) = &thread.inner.name {
+                return f(Some(name.as_str()));
+            } else if Some(thread.inner.id) == main_thread::get() {
+                // The main thread doesn't store its name in the handle, we must
+                // identify it through its ID. Since we already have the `Thread`,
+                // we can retrieve the ID from it instead of going through another
+                // thread local.
+                return f(Some("main"));
+            }
+        } else if let Some(main) = main_thread::get()
+            && let Some(id) = current::id::get()
+            && id == main
+        {
+            // The main thread doesn't always have a thread handle, we must
+            // identify it through its ID instead. The checks are ordered so
+            // that the current ID is only loaded if it is actually needed,
+            // since loading it from TLS might need multiple expensive accesses.
+            return f(Some("main"));
         }
+
+        f(None)
+    })
+}
+
+/// The internal representation of a `Thread` handle
+struct Inner {
+    name: Option<ThreadNameString>,
+    id: ThreadId,
+    parker: Parker,
+}
+
+impl Inner {
+    fn parker(self: Pin<&Self>) -> Pin<&Parker> {
+        unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
     }
 }
 
@@ -1390,47 +1412,21 @@ impl Inner {
 /// docs of [`Builder`] and [`spawn`] for more details.
 ///
 /// [`thread::current`]: current::current
-pub struct Thread(Inner);
+pub struct Thread {
+    inner: Pin<Arc<Inner>>,
+}
 
 impl Thread {
-    /// Used only internally to construct a thread object without spawning.
-    pub(crate) fn new(id: ThreadId, name: String) -> Thread {
-        Self::new_inner(id, Some(ThreadNameString::from(name)))
-    }
+    pub(crate) fn new(id: ThreadId, name: Option<String>) -> Thread {
+        let name = name.map(ThreadNameString::from);
 
-    pub(crate) fn new_unnamed(id: ThreadId) -> Thread {
-        Self::new_inner(id, None)
-    }
-
-    /// Used in runtime to construct main thread
-    ///
-    /// # 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: Option<ThreadNameString>) -> Thread {
         // We have to use `unsafe` here to construct the `Parker` in-place,
         // which is required for the UNIX implementation.
         //
         // SAFETY: We pin the Arc immediately after creation, so its address never
         // changes.
         let inner = unsafe {
-            let mut arc = Arc::<OtherInner>::new_uninit();
+            let mut arc = Arc::<Inner>::new_uninit();
             let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
             (&raw mut (*ptr).name).write(name);
             (&raw mut (*ptr).id).write(id);
@@ -1438,7 +1434,7 @@ impl Thread {
             Pin::new_unchecked(arc.assume_init())
         };
 
-        Self(Inner::Other(inner))
+        Thread { inner }
     }
 
     /// Like the public [`park`], but callable on any handle. This is used to
@@ -1447,7 +1443,7 @@ impl Thread {
     /// # Safety
     /// May only be called from the thread to which this handle belongs.
     pub(crate) unsafe fn park(&self) {
-        unsafe { self.0.parker().park() }
+        unsafe { self.inner.as_ref().parker().park() }
     }
 
     /// Like the public [`park_timeout`], but callable on any handle. This is
@@ -1456,7 +1452,7 @@ impl Thread {
     /// # 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) }
+        unsafe { self.inner.as_ref().parker().park_timeout(dur) }
     }
 
     /// Atomically makes the handle's token available if it is not already.
@@ -1492,7 +1488,7 @@ impl Thread {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn unpark(&self) {
-        self.0.parker().unpark();
+        self.inner.as_ref().parker().unpark();
     }
 
     /// Gets the thread's unique identifier.
@@ -1512,7 +1508,7 @@ impl Thread {
     #[stable(feature = "thread_id", since = "1.19.0")]
     #[must_use]
     pub fn id(&self) -> ThreadId {
-        self.0.id()
+        self.inner.id
     }
 
     /// Gets the thread's name.
@@ -1555,11 +1551,13 @@ impl Thread {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[must_use]
     pub fn name(&self) -> Option<&str> {
-        self.0.name()
-    }
-
-    fn cname(&self) -> Option<&CStr> {
-        self.0.cname()
+        if let Some(name) = &self.inner.name {
+            Some(name.as_str())
+        } else if main_thread::get() == Some(self.inner.id) {
+            Some("main")
+        } else {
+            None
+        }
     }
 
     /// Consumes the `Thread`, returning a raw pointer.
@@ -1583,7 +1581,9 @@ impl Thread {
     /// ```
     #[unstable(feature = "thread_raw", issue = "97523")]
     pub fn into_raw(self) -> *const () {
-        self.0.into_raw()
+        // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant.
+        let inner = unsafe { Pin::into_inner_unchecked(self.inner) };
+        Arc::into_raw(inner) as *const ()
     }
 
     /// Constructs a `Thread` from a raw pointer.
@@ -1605,7 +1605,17 @@ impl Thread {
     #[unstable(feature = "thread_raw", issue = "97523")]
     pub unsafe fn from_raw(ptr: *const ()) -> Thread {
         // Safety: Upheld by caller.
-        unsafe { Thread(Inner::from_raw(ptr)) }
+        unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } }
+    }
+
+    fn cname(&self) -> Option<&CStr> {
+        if let Some(name) = &self.inner.name {
+            Some(name.as_cstr())
+        } else if main_thread::get() == Some(self.inner.id) {
+            Some(c"main")
+        } else {
+            None
+        }
     }
 }
 
diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs
index 1535742a83a..df946cdcf2b 100644
--- a/library/std/tests/pipe_subprocess.rs
+++ b/library/std/tests/pipe_subprocess.rs
@@ -3,8 +3,7 @@
 fn main() {
     #[cfg(all(not(miri), any(unix, windows)))]
     {
-        use std::io::Read;
-        use std::pipe::pipe;
+        use std::io::{Read, pipe};
         use std::{env, process};
 
         if env::var("I_AM_THE_CHILD").is_ok() {