diff options
Diffstat (limited to 'library')
30 files changed, 400 insertions, 156 deletions
diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 052edf453f6..2c26f9e0312 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -18,7 +18,7 @@ use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem; -use core::ptr::{NonNull, Unique}; +use core::ptr::NonNull; use super::SpecExtend; use crate::alloc::{Allocator, Global}; @@ -168,15 +168,16 @@ impl<T, A: Allocator> LinkedList<T, A> { /// Adds the given node to the front of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed using the list's allocator. + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. #[inline] - unsafe fn push_front_node(&mut self, node: Unique<Node<T>>) { + unsafe fn push_front_node(&mut self, node: NonNull<Node<T>>) { // This method takes care not to create mutable references to whole nodes, // to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = self.head; (*node.as_ptr()).prev = None; - let node = Some(NonNull::from(node)); + let node = Some(node); match self.head { None => self.tail = node, @@ -212,15 +213,16 @@ impl<T, A: Allocator> LinkedList<T, A> { /// Adds the given node to the back of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed using the list's allocator. + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. #[inline] - unsafe fn push_back_node(&mut self, node: Unique<Node<T>>) { + unsafe fn push_back_node(&mut self, node: NonNull<Node<T>>) { // This method takes care not to create mutable references to whole nodes, // to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = None; (*node.as_ptr()).prev = self.tail; - let node = Some(NonNull::from(node)); + let node = Some(node); match self.tail { None => self.head = node, @@ -842,8 +844,8 @@ impl<T, A: Allocator> LinkedList<T, A> { #[stable(feature = "rust1", since = "1.0.0")] pub fn push_front(&mut self, elt: T) { let node = Box::new_in(Node::new(elt), &self.alloc); - let node_ptr = Unique::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked unsafe { self.push_front_node(node_ptr); } @@ -890,8 +892,8 @@ impl<T, A: Allocator> LinkedList<T, A> { #[stable(feature = "rust1", since = "1.0.0")] pub fn push_back(&mut self, elt: T) { let node = Box::new_in(Node::new(elt), &self.alloc); - let node_ptr = Unique::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked unsafe { self.push_back_node(node_ptr); } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 60b07485c3a..afed3fdf745 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2491,9 +2491,9 @@ impl<T, A: Allocator> From<Vec<T, A>> for Rc<[T], A> { /// /// ``` /// # use std::rc::Rc; - /// let original: Box<Vec<i32>> = Box::new(vec![1, 2, 3]); - /// let shared: Rc<Vec<i32>> = Rc::from(original); - /// assert_eq!(vec![1, 2, 3], *shared); + /// let unique: Vec<i32> = vec![1, 2, 3]; + /// let shared: Rc<[i32]> = Rc::from(unique); + /// assert_eq!(&[1, 2, 3], &shared[..]); /// ``` #[inline] fn from(v: Vec<T, A>) -> Rc<[T], A> { diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index f093a0990d1..5378b210e67 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -518,14 +518,14 @@ impl AsciiChar { /// Gets this ASCII character as a byte. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] - pub const fn as_u8(self) -> u8 { + pub const fn to_u8(self) -> u8 { self as u8 } /// Gets this ASCII character as a `char` Unicode Scalar Value. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] - pub const fn as_char(self) -> char { + pub const fn to_char(self) -> char { self as u8 as char } diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index 3d471419bb8..24bb9ad1ad1 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -95,11 +95,11 @@ impl<const N: usize> EscapeIterInner<N> { } pub fn next(&mut self) -> Option<u8> { - self.alive.next().map(|i| self.data[usize::from(i)].as_u8()) + self.alive.next().map(|i| self.data[usize::from(i)].to_u8()) } pub fn next_back(&mut self) -> Option<u8> { - self.alive.next_back().map(|i| self.data[usize::from(i)].as_u8()) + self.alive.next_back().map(|i| self.data[usize::from(i)].to_u8()) } pub fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> { diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5ec751e5168..aec287226a0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -76,8 +76,11 @@ macro marker_impls { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Send")] #[rustc_on_unimplemented( + on(_Self = "std::rc::Rc<T, A>", note = "use `std::sync::Arc` instead of `std::rc::Rc`"), message = "`{Self}` cannot be sent between threads safely", - label = "`{Self}` cannot be sent between threads safely" + label = "`{Self}` cannot be sent between threads safely", + note = "consider using `std::sync::Arc<{Self}>`; for more information visit \ + <https://doc.rust-lang.org/book/ch16-03-shared-state.html>" )] pub unsafe auto trait Send { // empty. @@ -628,8 +631,11 @@ impl<T: ?Sized> Copy for &T {} any(_Self = "core::cell::RefCell<T>", _Self = "std::cell::RefCell<T>"), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead", ), + on(_Self = "std::rc::Rc<T, A>", note = "use `std::sync::Arc` instead of `std::rc::Rc`"), message = "`{Self}` cannot be shared between threads safely", - label = "`{Self}` cannot be shared between threads safely" + label = "`{Self}` cannot be shared between threads safely", + note = "consider using `std::sync::Arc<{Self}>`; for more information visit \ + <https://doc.rust-lang.org/book/ch16-03-shared-state.html>" )] pub unsafe auto trait Sync { // FIXME(estebank): once support to add notes in `rustc_on_unimplemented` diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ddcd35b1ac7..5b213555394 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,21 +18,19 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true } -compiler_builtins = { version = "0.1.98" } +compiler_builtins = { version = "0.1.100" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -addr2line = { version = "0.20.0", optional = true, default-features = false } rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } -miniz_oxide = { version = "0.7.0", optional = true, default-features = false, public = false } -[dependencies.object] -version = "0.31.1" -optional = true -default-features = false -features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] +miniz_oxide = { version = "0.7.0", optional = true, default-features = false } +addr2line = { version = "0.20.0", optional = true, default-features = false } +object = { version = "0.31.1", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index bc532990e94..a53b8535213 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -957,4 +957,46 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f32; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f32 { + unsafe { cmath::tgammaf(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f32; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f32, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index e949def00bb..9ca4e8f2f45 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -653,6 +653,38 @@ fn test_atanh() { } #[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f32.gamma(), 1.0f32); + assert_approx_eq!(2.0f32.gamma(), 1.0f32); + assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32); + assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f32.gamma(), f32::INFINITY); + assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); + assert!((-1.0f32).gamma().is_nan()); + assert!((-2.0f32).gamma().is_nan()); + assert!(f32::NAN.gamma().is_nan()); + assert!(f32::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); + assert_eq!(171.71f32.gamma(), f32::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); + assert_eq!(1.0f32.ln_gamma().1, 1); + assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); + assert_eq!(2.0f32.ln_gamma().1, 1); + assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); + assert_eq!(3.0f32.ln_gamma().1, 1); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f32).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { use super::consts; diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 5af9f8bcb23..a1cec22c97a 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -957,4 +957,46 @@ impl f64 { pub fn atanh(self) -> f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f64; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f64 { + unsafe { cmath::tgamma(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f64; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f64, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgamma_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 53d351cceef..f88d01593b5 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -636,6 +636,38 @@ fn test_atanh() { } #[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f64.gamma(), 1.0f64); + assert_approx_eq!(2.0f64.gamma(), 1.0f64); + assert_approx_eq!(3.0f64.gamma(), 2.0f64); + assert_approx_eq!(4.0f64.gamma(), 6.0f64); + assert_approx_eq!(5.0f64.gamma(), 24.0f64); + assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f64.gamma(), f64::INFINITY); + assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); + assert!((-1.0f64).gamma().is_nan()); + assert!((-2.0f64).gamma().is_nan()); + assert!(f64::NAN.gamma().is_nan()); + assert!(f64::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); + assert_eq!(171.71f64.gamma(), f64::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); + assert_eq!(1.0f64.ln_gamma().1, 1); + assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); + assert_eq!(2.0f64.ln_gamma().1, 1); + assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); + assert_eq!(3.0f64.ln_gamma().1, 1); + assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f64).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { use super::consts; let pi: f64 = consts::PI; diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0f79e74f555..a7e65305386 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -791,6 +791,7 @@ impl Write for &File { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { self.inner.flush() } @@ -836,6 +837,7 @@ impl Write for File { fn is_write_vectored(&self) -> bool { (&&*self).is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { (&*self).flush() } @@ -881,6 +883,7 @@ impl Write for Arc<File> { fn is_write_vectored(&self) -> bool { (&**self).is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { (&**self).flush() } diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index 1f3f80b9618..034ddd8df9a 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -310,6 +310,7 @@ impl<'a> Write for BorrowedCursor<'a> { Ok(buf.len()) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 31481de8495..9038e8fa9d7 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -288,6 +288,7 @@ #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] #![feature(extend_one)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] @@ -396,9 +397,15 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; +// FIXME: #94122 this extern crate definition only exist here to stop +// miniz_oxide docs leaking into std docs. Find better way to do it. +// Remove exclusion from tidy platform check when this removed. #[doc(masked)] #[allow(unused_extern_crates)] -#[cfg(feature = "miniz_oxide")] +#[cfg(all( + not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))), + feature = "miniz_oxide" +))] extern crate miniz_oxide; // During testing, this crate is not actually the "real" std library, but rather diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 141a18a42dd..32fd54c8e75 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -647,6 +647,7 @@ impl Write for TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -685,6 +686,7 @@ impl Write for &TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index e20170873bb..41290e0017a 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -712,6 +712,7 @@ impl<'a> io::Write for &'a UnixStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index f9cb755b01a..f25ad2ece71 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -280,6 +280,7 @@ impl Write for ChildStdin { io::Write::is_write_vectored(&&*self) } + #[inline] fn flush(&mut self) -> io::Result<()> { (&*self).flush() } @@ -299,6 +300,7 @@ impl Write for &ChildStdin { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -1528,7 +1530,7 @@ impl From<fs::File> for Stdio { // vs `_exit`. Naming of Unix system calls is not standardised across Unices, so terminology is a // matter of convention and tradition. For clarity we usually speak of `exit`, even when we might // mean an underlying system call such as `_exit`. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] #[stable(feature = "process", since = "1.0.0")] pub struct ExitStatus(imp::ExitStatus); diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index e39254aa434..ed3c5512084 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -130,11 +130,8 @@ impl Barrier { let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - // We need a while loop to guard against spurious wakeups. - // https://en.wikipedia.org/wiki/Spurious_wakeup - while local_gen == lock.generation_id { - lock = self.cvar.wait(lock).unwrap(); - } + let _guard = + self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap(); BarrierWaitResult(false) } else { lock.count = 0; diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 4bb735668d2..6aa4ea7f5b4 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -335,6 +335,7 @@ impl File { false } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs index 2bf80d7a4cb..5346d229116 100644 --- a/library/std/src/sys/unix/cmath.rs +++ b/library/std/src/sys/unix/cmath.rs @@ -30,4 +30,8 @@ extern "C" { pub fn tanf(n: f32) -> f32; pub fn tanh(n: f64) -> f64; pub fn tanhf(n: f32) -> f32; + pub fn tgamma(n: f64) -> f64; + pub fn tgammaf(n: f32) -> f32; + pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 7dc809a038b..a5604c92a80 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1204,6 +1204,7 @@ impl File { self.0.write_vectored_at(bufs, offset) } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index e45c380a0bb..9931c2af2f1 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -235,7 +235,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(i64); impl ExitStatus { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 0ce93af66ac..3963e7f52d5 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -10,9 +10,6 @@ use core::ffi::NonZero_c_int; #[cfg(target_os = "linux")] use crate::os::linux::process::PidFd; -#[cfg(target_os = "linux")] -use crate::sys::weak::raw_syscall; - #[cfg(any( target_os = "macos", target_os = "watchos", @@ -91,6 +88,11 @@ impl Command { if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { return Ok((ret, ours)); } + + #[cfg(target_os = "linux")] + let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; + + #[cfg(not(target_os = "linux"))] let (input, output) = sys::pipe::anon_pipe()?; // Whatever happens after the fork is almost for sure going to touch or @@ -104,12 +106,16 @@ impl Command { // The child calls `mem::forget` to leak the lock, which is crucial because // releasing a lock is not async-signal-safe. let env_lock = sys::os::env_read_lock(); - let (pid, pidfd) = unsafe { self.do_fork()? }; + let pid = unsafe { self.do_fork()? }; if pid == 0 { crate::panic::always_abort(); mem::forget(env_lock); // avoid non-async-signal-safe unlocking drop(input); + #[cfg(target_os = "linux")] + if self.get_create_pidfd() { + self.send_pidfd(&output); + } let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; let errno = errno.to_be_bytes(); @@ -133,6 +139,12 @@ impl Command { drop(env_lock); drop(output); + #[cfg(target_os = "linux")] + let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; + + #[cfg(not(target_os = "linux"))] + let pidfd = -1; + // Safety: We obtained the pidfd from calling `clone3` with // `CLONE_PIDFD` so it's valid an otherwise unowned. let mut p = unsafe { Process::new(pid, pidfd) }; @@ -160,6 +172,7 @@ impl Command { } Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + // similarly SOCK_SEQPACKET messages should arrive whole assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); panic!("short read on the CLOEXEC pipe") } @@ -185,20 +198,19 @@ impl Command { ); #[cfg(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); } // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. #[cfg(not(any( - target_os = "linux", target_os = "watchos", target_os = "tvos", all(target_os = "nto", target_env = "nto71"), )))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { - cvt(libc::fork()).map(|res| (res, -1)) + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { + cvt(libc::fork()) } // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened @@ -206,7 +218,7 @@ impl Command { // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html #[cfg(all(target_os = "nto", target_env = "nto71"))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { use crate::sys::os::errno; let mut delay = MIN_FORKSPAWN_SLEEP; @@ -229,91 +241,11 @@ impl Command { delay *= 2; continue; } else { - return cvt(r).map(|res| (res, -1)); + return cvt(r); } } } - // Attempts to fork the process. If successful, returns Ok((0, -1)) - // in the child, and Ok((child_pid, child_pidfd)) in the parent. - #[cfg(target_os = "linux")] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { - use crate::sync::atomic::{AtomicBool, Ordering}; - - static HAS_CLONE3: AtomicBool = AtomicBool::new(true); - const CLONE_PIDFD: u64 = 0x00001000; - - #[repr(C)] - struct clone_args { - flags: u64, - pidfd: u64, - child_tid: u64, - parent_tid: u64, - exit_signal: u64, - stack: u64, - stack_size: u64, - tls: u64, - set_tid: u64, - set_tid_size: u64, - cgroup: u64, - } - - raw_syscall! { - fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long - } - - // Bypassing libc for `clone3` can make further libc calls unsafe, - // so we use it sparingly for now. See #89522 for details. - // Some tools (e.g. sandboxing tools) may also expect `fork` - // rather than `clone3`. - let want_clone3_pidfd = self.get_create_pidfd(); - - // If we fail to create a pidfd for any reason, this will - // stay as -1, which indicates an error. - let mut pidfd: pid_t = -1; - - // Attempt to use the `clone3` syscall, which supports more arguments - // (in particular, the ability to create a pidfd). If this fails, - // we will fall through this block to a call to `fork()` - if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) { - let mut args = clone_args { - flags: CLONE_PIDFD, - pidfd: &mut pidfd as *mut pid_t as u64, - child_tid: 0, - parent_tid: 0, - exit_signal: libc::SIGCHLD as u64, - stack: 0, - stack_size: 0, - tls: 0, - set_tid: 0, - set_tid_size: 0, - cgroup: 0, - }; - - let args_ptr = &mut args as *mut clone_args; - let args_size = crate::mem::size_of::<clone_args>(); - - let res = cvt(clone3(args_ptr, args_size)); - match res { - Ok(n) => return Ok((n as pid_t, pidfd)), - Err(e) => match e.raw_os_error() { - // Multiple threads can race to execute this store, - // but that's fine - that just means that multiple threads - // will have tried and failed to execute the same syscall, - // with no other side effects. - Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed), - // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp) - Some(libc::EPERM) => {} - _ => return Err(e), - }, - } - } - - // Generally, we just call `fork`. If we get here after wanting `clone3`, - // then the syscall does not exist or we do not have permission to call it. - cvt(libc::fork()).map(|res| (res, pidfd)) - } - pub fn exec(&mut self, default: Stdio) -> io::Error { let envp = self.capture_env(); @@ -722,6 +654,115 @@ impl Command { Ok(Some(p)) } } + + #[cfg(target_os = "linux")] + fn send_pidfd(&self, sock: &crate::sys::net::Socket) { + use crate::io::IoSlice; + use crate::os::fd::RawFd; + use crate::sys::cvt_r; + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + let child_pid = libc::getpid(); + // pidfd_open sets CLOEXEC by default + let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); + + let fds: [c_int; 1] = [pidfd as RawFd]; + + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + + let mut cmsg: Cmsg = mem::zeroed(); + + // 0-length message to send through the socket so we can pass along the fd + let mut iov = [IoSlice::new(b"")]; + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; + msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; + + // only attach cmsg if we successfully acquired the pidfd + if pidfd >= 0 { + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + (*hdr).cmsg_level = SOL_SOCKET; + (*hdr).cmsg_type = SCM_RIGHTS; + (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; + let data = CMSG_DATA(hdr); + crate::ptr::copy_nonoverlapping( + fds.as_ptr().cast::<u8>(), + data as *mut _, + SCM_MSG_LEN, + ); + } + + // we send the 0-length message even if we failed to acquire the pidfd + // so we get a consistent SEQPACKET order + match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { + Ok(0) => {} + _ => rtabort!("failed to communicate with parent process"), + } + } + } + + #[cfg(target_os = "linux")] + fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { + use crate::io::IoSliceMut; + use crate::sys::cvt_r; + + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + let mut cmsg: Cmsg = mem::zeroed(); + // 0-length read to get the fd + let mut iov = [IoSliceMut::new(&mut [])]; + + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = mem::size_of::<Cmsg>() as _; + msg.msg_control = &mut cmsg as *mut _ as *mut _; + + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, 0)) { + Err(_) => return -1, + Ok(_) => {} + } + + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + if hdr.is_null() + || (*hdr).cmsg_level != SOL_SOCKET + || (*hdr).cmsg_type != SCM_RIGHTS + || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ + { + return -1; + } + let data = CMSG_DATA(hdr); + + let mut fds = [-1 as c_int]; + + crate::ptr::copy_nonoverlapping( + data as *const _, + fds.as_mut_ptr().cast::<u8>(), + SCM_MSG_LEN, + ); + + fds[0] + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -800,7 +841,7 @@ impl Process { // // This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". // See the discussion in comments and doc comments for `std::process::ExitStatus`. -#[derive(PartialEq, Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Default)] pub struct ExitStatus(c_int); impl fmt::Debug for ExitStatus { diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index e5e1f956bc3..6aa79e7f9e7 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -60,3 +60,28 @@ fn test_command_fork_no_unwind() { || signal == libc::SIGSEGV ); } + +#[test] +#[cfg(target_os = "linux")] +fn test_command_pidfd() { + use crate::os::fd::RawFd; + use crate::os::linux::process::{ChildExt, CommandExt}; + use crate::process::Command; + + let our_pid = crate::process::id(); + let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; + let pidfd_open_available = if pidfd >= 0 { + unsafe { libc::close(pidfd as RawFd) }; + true + } else { + false + }; + + // always exercise creation attempts + let child = Command::new("echo").create_pidfd(true).spawn().unwrap(); + + // but only check if we know that the kernel supports pidfds + if pidfd_open_available { + assert!(child.pidfd().is_ok()) + } +} diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs index f28ca58d020..8e0b971af73 100644 --- a/library/std/src/sys/unix/process/process_unsupported.rs +++ b/library/std/src/sys/unix/process/process_unsupported.rs @@ -55,7 +55,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c_int); impl ExitStatus { diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index f70d3cb396b..1ff2b2fb383 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -179,7 +179,7 @@ impl Process { } /// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c_int); impl ExitStatus { diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index a26f20795a1..97e75f1b5b6 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -54,6 +54,7 @@ impl io::Write for Stdout { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -81,6 +82,7 @@ impl io::Write for Stderr { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index a494f2d6b4c..77b675aaa4e 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -99,58 +99,59 @@ impl fmt::Debug for Command { } } -pub struct ExitStatus(!); +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +#[non_exhaustive] +pub struct ExitStatus(); impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - self.0 + Ok(()) } pub fn code(&self) -> Option<i32> { - self.0 + Some(0) } } -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - self.0 +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "<dummy exit status>") } } -impl Copy for ExitStatus {} +pub struct ExitStatusError(!); -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { +impl Clone for ExitStatusError { + fn clone(&self) -> ExitStatusError { self.0 } } -impl Eq for ExitStatus {} +impl Copy for ExitStatusError {} -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl PartialEq for ExitStatusError { + fn eq(&self, _other: &ExitStatusError) -> bool { self.0 } } -impl fmt::Display for ExitStatus { +impl Eq for ExitStatusError {} + +impl fmt::Debug for ExitStatusError { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(ExitStatus); - impl Into<ExitStatus> for ExitStatusError { fn into(self) -> ExitStatus { - self.0.0 + self.0 } } impl ExitStatusError { pub fn code(self) -> Option<NonZeroI32> { - self.0.0 + self.0 } } diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 43ab8c7ee65..1b2a86f3c0e 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -1,6 +1,6 @@ #![cfg(not(test))] -use libc::{c_double, c_float}; +use libc::{c_double, c_float, c_int}; extern "C" { pub fn acos(n: c_double) -> c_double; @@ -23,6 +23,10 @@ extern "C" { pub fn sinh(n: c_double) -> c_double; pub fn tan(n: c_double) -> c_double; pub fn tanh(n: c_double) -> c_double; + pub fn tgamma(n: c_double) -> c_double; + pub fn tgammaf(n: c_float) -> c_float; + pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; + pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; } pub use self::shims::*; diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index e3493cbb850..2dd0c67acdb 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -652,7 +652,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c::DWORD); impl ExitStatus { diff --git a/library/test/src/term/terminfo/searcher/tests.rs b/library/test/src/term/terminfo/searcher/tests.rs index 4227a585e2f..e1edd3b25cf 100644 --- a/library/test/src/term/terminfo/searcher/tests.rs +++ b/library/test/src/term/terminfo/searcher/tests.rs @@ -6,14 +6,12 @@ fn test_get_dbpath_for_term() { // woefully inadequate test coverage // note: current tests won't work with non-standard terminfo hierarchies (e.g., macOS's) use std::env; - // FIXME (#9639): This needs to handle non-utf8 paths - fn x(t: &str) -> String { - let p = get_dbpath_for_term(t).expect("no terminfo entry found"); - p.to_str().unwrap().to_string() + fn x(t: &str) -> PathBuf { + get_dbpath_for_term(t).expect(&format!("no terminfo entry found for {t:?}")) } - assert!(x("screen") == "/usr/share/terminfo/s/screen"); - assert!(get_dbpath_for_term("") == None); + assert_eq!(x("screen"), PathBuf::from("/usr/share/terminfo/s/screen")); + assert_eq!(get_dbpath_for_term(""), None); env::set_var("TERMINFO_DIRS", ":"); - assert!(x("screen") == "/usr/share/terminfo/s/screen"); + assert_eq!(x("screen"), PathBuf::from("/usr/share/terminfo/s/screen")); env::remove_var("TERMINFO_DIRS"); } |
