diff options
| author | Jakub Beránek <berykubik@gmail.com> | 2025-01-20 14:12:41 +0100 |
|---|---|---|
| committer | Jakub Beránek <berykubik@gmail.com> | 2025-01-20 14:12:41 +0100 |
| commit | 1e0204beae7c0ebc5cddc64d1375bc7ee95f41db (patch) | |
| tree | 35fe35cfae96730b99e09b4dd97fdff8ab3e3a64 /library/std/src | |
| parent | 808bd955862a48f604f1b227c7ca0bbb7ae2a8e8 (diff) | |
| parent | ecda83b30f0f68cf5692855dddc0bc38ee8863fc (diff) | |
Merge from rustc
Diffstat (limited to 'library/std/src')
49 files changed, 1007 insertions, 701 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 56d734ba2fb..d2342d8fd51 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -296,10 +296,7 @@ impl<K, V, S> HashMap<K, V, S> { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hash_builder: S) -> HashMap<K, V, S> { HashMap { base: base::HashMap::with_hasher(hash_builder) } } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index bcf6d6b4ccf..bbb6ca23521 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -388,10 +388,7 @@ impl<T, S> HashSet<T, S> { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hasher: S) -> HashSet<T, S> { HashSet { base: base::HashSet::with_hasher(hasher) } } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 535236ff89a..11a29cdae62 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -622,7 +622,7 @@ impl Error for JoinPathsError { /// /// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. /// -/// Before Rust CURRENT_RUSTC_VERSION, this function used to return the value of the 'HOME' environment variable +/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable /// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` /// instead of `C:\Users\you`. /// diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index cbec44c8626..2572b71fd9a 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -295,7 +295,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); + assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box<OsStr> = OsStr::new("world.exe").into(); assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b)); diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 17721090db5..5251cc302cb 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -50,7 +50,7 @@ impl Buffer { pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and // that region is initialized because those are all invariants of this type. - unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) } + unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } #[inline] diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 476c403c21f..38b72336617 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -339,7 +339,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] QuotaExceeded, /// File larger than allowed or supported. /// @@ -364,7 +364,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index f958a938646..716da37168d 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -103,7 +103,8 @@ //! the time. use core::marker::PhantomData; -use core::ptr::{self, NonNull}; +use core::num::NonZeroUsize; +use core::ptr::NonNull; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; @@ -176,7 +177,7 @@ impl Repr { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -193,7 +194,7 @@ impl Repr { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will 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 2f8f5c5c581..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)] @@ -342,6 +341,7 @@ #![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] +#![feature(nonnull_provenance)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -357,6 +357,7 @@ #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] +#![feature(temporary_niche_types)] #![feature(ub_checks)] #![feature(used_with_arg)] // tidy-alphabetical-end @@ -594,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/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index abb13b75f50..1e814eca3c1 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -11,6 +11,8 @@ use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, fs, io}; +type ValidRawFd = core::num::niche_types::NotAllOnes<RawFd>; + /// A borrowed file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that owns the file @@ -32,15 +34,10 @@ use crate::{fmt, fs, io}; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedFd<'fd> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'fd OwnedFd>, } @@ -56,15 +53,10 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -80,7 +72,8 @@ impl BorrowedFd<'_> { pub const unsafe fn borrow_raw(fd: RawFd) -> Self { assert!(fd != u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -130,7 +123,7 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -138,7 +131,7 @@ impl AsRawFd for BorrowedFd<'_> { impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -146,7 +139,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -164,7 +157,8 @@ impl FromRawFd for OwnedFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } @@ -187,12 +181,12 @@ impl Drop for OwnedFd { #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] - crate::sys::fs::debug_assert_fd_is_open(self.fd); + crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner()); - let _ = libc::close(self.fd); + let _ = libc::close(self.fd.as_inner()); } #[cfg(target_os = "hermit")] - let _ = hermit_abi::close(self.fd); + let _ = hermit_abi::close(self.fd.as_inner()); } } } diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 22f5528248a..03dff94350d 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -19,11 +19,9 @@ use crate::sys_common::{AsInner, IntoInner}; use crate::{fs, io}; /// Raw file descriptors. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_os = "hermit")] pub type RawFd = i32; @@ -33,7 +31,6 @@ pub type RawFd = i32; /// This is only available on unix and WASI platforms and must be imported in /// order to call the method. Windows platforms have a corresponding /// `AsRawHandle` and `AsRawSocket` set of traits. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -67,7 +64,6 @@ pub trait AsRawFd { /// A trait to express the ability to construct an object from a raw file /// descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file @@ -112,7 +108,6 @@ pub trait FromRawFd { /// A trait to express the ability to consume an object and acquire ownership of /// its raw file descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "into_raw_os", since = "1.4.0")] pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 2d18f339615..b8601b533fe 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -54,6 +54,9 @@ use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; +// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +type ValidRawFd = core::num::niche_types::NotAllOnes<RawFd>; + /// A borrowed SOLID Sockets file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -69,12 +72,9 @@ pub type RawFd = i32; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'socket OwnedFd>, } @@ -87,12 +87,9 @@ pub struct BorrowedFd<'socket> { /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -108,7 +105,8 @@ impl BorrowedFd<'_> { assert!(fd != -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd, _phantom: PhantomData } } } @@ -132,21 +130,21 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -162,14 +160,15 @@ impl FromRawFd for OwnedFd { assert_ne!(fd, -1 as RawFd); // SAFETY: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + let fd = unsafe { ValidRawFd::new_unchecked(fd) }; + Self { fd } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { - unsafe { sys::net::netc::close(self.fd) }; + unsafe { sys::net::netc::close(self.fd.as_inner()) }; } } diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs deleted file mode 100644 index 930aca887e3..00000000000 --- a/library/std/src/os/wasi/io/fd.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Owned and borrowed file descriptors. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// Tests for this module -#[cfg(test)] -mod tests; - -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 4e123a1eec8..5f9a735db08 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -4,3 +4,7 @@ #[stable(feature = "io_safety_wasi", since = "1.65.0")] pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs deleted file mode 100644 index da3b36adad4..00000000000 --- a/library/std/src/os/wasi/io/raw.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! WASI-specific extensions to general I/O primitives. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// NOTE: despite the fact that this module is unstable, -// stable Rust had the capability to access the stable -// re-exported items from os::fd::raw through this -// unstable module. -// In PR #95956 the stability checker was changed to check -// all path segments of an item rather than just the last, -// which caused the aforementioned stable usage to regress -// (see issue #99502). -// As a result, the items in os::fd::raw were given the -// rustc_allowed_through_unstable_modules attribute. -// No regression tests were added to ensure this property, -// as CI is not configured to test wasm32-wasi. -// If this module is stabilized, -// you may want to remove those attributes -// (assuming no other unstable modules need them). -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/tests.rs index 418274752b0..418274752b0 100644 --- a/library/std/src/os/wasi/io/fd/tests.rs +++ b/library/std/src/os/wasi/io/tests.rs diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index c6d7bad9440..6e13a8b502a 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -9,6 +9,9 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::cvt; use crate::{fmt, io, sys}; +// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`. +type ValidRawSocket = core::num::niche_types::NotAllOnes<RawSocket>; + /// A borrowed socket. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -24,17 +27,10 @@ use crate::{fmt, io, sys}; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedSocket<'socket> { - socket: RawSocket, + socket: ValidRawSocket, _phantom: PhantomData<&'socket OwnedSocket>, } @@ -47,17 +43,10 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedSocket { - socket: RawSocket, + socket: ValidRawSocket, } impl BorrowedSocket<'_> { @@ -73,7 +62,8 @@ impl BorrowedSocket<'_> { #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - unsafe { Self { socket, _phantom: PhantomData } } + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket, _phantom: PhantomData } } } @@ -172,7 +162,7 @@ fn last_error() -> io::Error { impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -180,7 +170,7 @@ impl AsRawSocket for BorrowedSocket<'_> { impl AsRawSocket for OwnedSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -188,7 +178,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - ManuallyDrop::new(self).socket + ManuallyDrop::new(self).socket.as_inner() } } @@ -196,10 +186,9 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - unsafe { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - Self { socket } - } + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); + let socket = unsafe { ValidRawSocket::new_unchecked(socket) }; + Self { socket } } } @@ -208,7 +197,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); + let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET); } } } 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/path.rs b/library/std/src/path.rs index 35e920ab344..7fd08a97f1f 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -298,7 +298,7 @@ where } // Detect scheme on Redox -fn has_redox_scheme(s: &[u8]) -> bool { +pub(crate) fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') } @@ -2155,7 +2155,7 @@ impl Path { unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. - fn as_u8_slice(&self) -> &[u8] { + pub(crate) fn as_u8_slice(&self) -> &[u8] { self.inner.as_encoded_bytes() } @@ -2323,14 +2323,7 @@ impl Path { #[must_use] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - self.has_root() || has_redox_scheme(self.as_u8_slice()) - } else { - self.has_root() - && (cfg!(any(unix, target_os = "hermit", target_os = "wasi")) - || self.prefix().is_some()) - } + sys::path::is_absolute(self) } /// Returns `true` if the `Path` is relative, i.e., not absolute. @@ -2353,7 +2346,7 @@ impl Path { !self.is_absolute() } - fn prefix(&self) -> Option<Prefix<'_>> { + pub(crate) fn prefix(&self) -> Option<Prefix<'_>> { self.components().prefix } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index ff3f7151bb8..3f96ac4672a 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -2069,9 +2069,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<Path>(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { - MaybeUninit::slice_assume_init_ref(&storage) - }); + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box<Path> = Path::new("world.exe").into(); assert_eq!(size_of_val::<Path>(a), size_of_val::<Path>(&b)); 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/prelude/common.rs b/library/std/src/prelude/common.rs index 22a364074c5..0f2d8334fca 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -12,8 +12,7 @@ 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}; -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[doc(no_inline)] pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 64349987fcf..4ec328208f0 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -169,12 +169,12 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b2492238bd3..3a22a16cb16 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: @@ -32,9 +32,14 @@ use crate::{mem, panic, sys}; // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { + #[cfg(not(feature = "panic_immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } + #[cfg(feature = "panic_immediate_abort")] + { + let _ = format_args!($($t)*); + } } } @@ -67,7 +72,7 @@ macro_rules! rtunwrap { }; } -fn handle_rt_panic(e: Box<dyn Any + Send>) { +fn handle_rt_panic<T>(e: Box<dyn Any + Send>) -> T { mem::forget(e); rtabort!("initialization or cleanup bug"); } @@ -102,24 +107,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 @@ -157,7 +147,7 @@ fn lang_start_internal( argc: isize, argv: *const *const u8, sigpipe: u8, -) -> Result<isize, !> { +) -> isize { // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -168,19 +158,33 @@ fn lang_start_internal( // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. - // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) - .unwrap_or_else(handle_rt_panic); - let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) - .map_err(move |e| { - mem::forget(e); - rtabort!("drop of the panic payload panicked"); + // + // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in + // case of a panic a bit nicer. + panic::catch_unwind(move || { + // SAFETY: Only called once during runtime initialization. + unsafe { init(argc, argv, sigpipe) }; + + let ret_code = panic::catch_unwind(main).unwrap_or_else(move |payload| { + // Carefully dispose of the panic payload. + let payload = panic::AssertUnwindSafe(payload); + panic::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| { + mem::forget(e); // do *not* drop the 2nd payload + rtabort!("drop of the panic payload panicked"); + }); + // Return error code for panicking programs. + 101 }); - panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); - // Guard against multiple threads calling `libc::exit` concurrently. - // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); - ret_code + let ret_code = ret_code as isize; + + cleanup(); + // Guard against multiple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + crate::sys::exit_guard::unique_thread_exit(); + + ret_code + }) + .unwrap_or_else(handle_rt_panic) } #[cfg(not(any(test, doctest)))] @@ -191,11 +195,10 @@ fn lang_start<T: crate::process::Termination + 'static>( argv: *const *const u8, sigpipe: u8, ) -> isize { - let Ok(v) = lang_start_internal( + lang_start_internal( &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, - ); - v + ) } 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/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index 04dd10ad806..fa2e470d6b6 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -12,15 +12,12 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::exists; use crate::sys_common::ignore_notfound; +type CIntNotMinusOne = core::num::niche_types::NotAllOnes<c_int>; + /// A file descriptor. #[derive(Clone, Copy)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] struct FileDesc { - fd: c_int, + fd: CIntNotMinusOne, } impl FileDesc { @@ -29,12 +26,13 @@ impl FileDesc { assert_ne!(fd, -1i32); // Safety: we just asserted that the value is in the valid range and // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } + let fd = unsafe { CIntNotMinusOne::new_unchecked(fd) }; + FileDesc { fd } } #[inline] fn raw(&self) -> c_int { - self.fd + self.fd.as_inner() } } diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs new file mode 100644 index 00000000000..9585ec24f68 --- /dev/null +++ b/library/std/src/sys/pal/uefi/fs.rs @@ -0,0 +1,344 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result<SystemTime> { + self.0 + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + self.0 + } + + pub fn created(&self) -> io::Result<SystemTime> { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash<H: Hasher>(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + self.0 + } + + pub fn file_type(&self) -> io::Result<FileType> { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> { + unsupported() + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result<bool> { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result<bool> { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> { + self.0 + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result<ReadDir> { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result<bool> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result<PathBuf> { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result<FileAttr> { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result<FileAttr> { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> { + unsupported() +} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 47d9add72b5..7504a0f7ad7 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -222,14 +222,14 @@ pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> NonNull::new(runtime_services) } -pub(crate) struct DevicePath(NonNull<r_efi::protocols::device_path::Protocol>); +pub(crate) struct OwnedDevicePath(NonNull<r_efi::protocols::device_path::Protocol>); -impl DevicePath { +impl OwnedDevicePath { pub(crate) fn from_text(p: &OsStr) -> io::Result<Self> { fn inner( p: &OsStr, protocol: NonNull<r_efi::protocols::device_path_from_text::Protocol>, - ) -> io::Result<DevicePath> { + ) -> io::Result<OwnedDevicePath> { let path_vec = p.encode_wide().chain(Some(0)).collect::<Vec<u16>>(); if path_vec[..path_vec.len() - 1].contains(&0) { return Err(const_error!( @@ -242,7 +242,7 @@ impl DevicePath { unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; NonNull::new(path) - .map(DevicePath) + .map(OwnedDevicePath) .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")) } @@ -275,12 +275,12 @@ impl DevicePath { )) } - pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } } -impl Drop for DevicePath { +impl Drop for OwnedDevicePath { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull<r_efi::efi::BootServices> = bt.cast(); @@ -291,6 +291,15 @@ impl Drop for DevicePath { } } +impl crate::fmt::Debug for OwnedDevicePath { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match device_path_to_text(self.0) { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol<T> { guid: r_efi::efi::Guid, handle: NonNull<crate::ffi::c_void>, diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index f29c91f3bfe..111bed7a7eb 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,7 +15,6 @@ pub mod args; pub mod env; -#[path = "../unsupported/fs.rs"] pub mod fs; pub mod helpers; #[path = "../unsupported/io.rs"] diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 95707ebb7f0..1a0754134df 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -326,7 +326,7 @@ mod uefi_command_internal { impl Image { pub fn load_image(p: &OsStr) -> io::Result<Self> { - let path = helpers::DevicePath::from_text(p)?; + let path = helpers::OwnedDevicePath::from_text(p)?; let boot_services: NonNull<r_efi::efi::BootServices> = boot_services() .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); 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/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e360ba0f6d7..f657f82e6e3 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -130,25 +130,27 @@ impl Thread { } } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))] pub fn set_name(name: &CStr) { - const TASK_COMM_LEN: usize = 16; - unsafe { - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Linux limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } else { + // FreeBSD and DragonFly BSD do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. debug_assert_eq!(res, 0); } } - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "nuttx" - ))] + #[cfg(any(target_os = "openbsd", target_os = "nuttx"))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 343864d0b3f..e224980e95f 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,3 +1,5 @@ +use core::num::niche_types::Nanoseconds; + use crate::time::Duration; use crate::{fmt, io}; @@ -16,12 +18,6 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(crate) t: Timespec, } @@ -59,14 +55,14 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } impl Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } } pub const fn zero() -> Timespec { @@ -147,12 +143,15 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { - ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) + let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { + ( + (self.tv_sec - other.tv_sec) as u64, + self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), + ) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, + self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; @@ -170,7 +169,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.0; + let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner(); if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -182,7 +181,7 @@ impl Timespec { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -194,7 +193,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option<libc::timespec> { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.0.try_into().ok()?, + tv_nsec: self.tv_nsec.as_inner().try_into().ok()?, }) } @@ -203,7 +202,7 @@ impl Timespec { #[cfg(target_os = "nto")] pub(in crate::sys) fn to_timespec_capped(&self) -> Option<libc::timespec> { // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.0 as u64) + if (self.tv_nsec.as_inner() as u64) .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) .is_none() { @@ -219,7 +218,7 @@ impl Timespec { not(target_arch = "riscv32") ))] pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) + __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _) } } @@ -293,7 +292,7 @@ impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 88e6def7a75..4282dbb5493 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -272,7 +272,7 @@ where unreachable!(); } else { // Safety: First `k` values are initialized. - let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + let slice: &[u16] = buf[..k].assume_init_ref(); return Ok(f2(slice)); } } 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/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 1b735e7f0cb..fd3f559ba19 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -207,7 +207,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz assert!(result != 0, "Unexpected error in MultiByteToWideChar"); // Safety: MultiByteToWideChar initializes `result` values. - MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) + utf16[..result as usize].assume_init_ref() }; let mut written = write_u16s(handle, utf16)?; @@ -283,7 +283,7 @@ impl io::Read for Stdin { let read = read_u16s_fixup_surrogates(handle, &mut utf16_buf, 1, &mut self.surrogate)?; // Read bytes, using the (now-empty) self.incomplete_utf8 as extra space. let read_bytes = utf16_to_utf8( - unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }, + unsafe { utf16_buf[..read].assume_init_ref() }, &mut self.incomplete_utf8.bytes, )?; @@ -303,7 +303,7 @@ impl io::Read for Stdin { read_u16s_fixup_surrogates(handle, &mut utf16_buf, amount, &mut self.surrogate)?; // Safety `read_u16s_fixup_surrogates` returns the number of items // initialized. - let utf16s = unsafe { MaybeUninit::slice_assume_init_ref(&utf16_buf[..read]) }; + let utf16s = unsafe { utf16_buf[..read].assume_init_ref() }; match utf16_to_utf8(utf16s, buf) { Ok(value) => return Ok(bytes_copied + value), Err(e) => return Err(e), diff --git a/library/std/src/sys/path/sgx.rs b/library/std/src/sys/path/sgx.rs index c805c15e702..32c7752f605 100644 --- a/library/std/src/sys/path/sgx.rs +++ b/library/std/src/sys/path/sgx.rs @@ -23,3 +23,7 @@ pub const MAIN_SEP: char = '/'; pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 2a7c025c3c4..361e99964f1 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -60,3 +60,14 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { Ok(normalized) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + if cfg!(target_os = "redox") { + // FIXME: Allow Redox prefixes + path.has_root() || crate::path::has_redox_scheme(path.as_u8_slice()) + } else if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + path.has_root() + } else { + path.has_root() && path.prefix().is_some() + } +} diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 855f443678c..30b06c132c9 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -24,3 +24,7 @@ pub const MAIN_SEP: char = '\\'; pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index de042fa3f82..1c534721916 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -346,3 +346,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { os2path, ) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} 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(¤t)) } } 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 85ee369ca2b..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.into()), - 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 + } } } |
