diff --git a/Cargo.lock b/Cargo.lock index 8f60cea459c7d..accbbe9d236ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "moto-rt" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +checksum = "0bf4bc387d3b3502cb92c09ec980cca909b94978e144c61da8319ecf4bc8d031" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/alloc/src/collections/btree/append.rs b/alloc/src/collections/btree/append.rs index 091376d5d685b..66ea22e75247c 100644 --- a/alloc/src/collections/btree/append.rs +++ b/alloc/src/collections/btree/append.rs @@ -104,9 +104,14 @@ where { type Item = (K, V); - /// If two keys are equal, returns the key-value pair from the right source. + /// If two keys are equal, returns the key from the left and the value from the right. fn next(&mut self) -> Option<(K, V)> { let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0)); - b_next.or(a_next) + match (a_next, b_next) { + (Some((a_k, _)), Some((_, b_v))) => Some((a_k, b_v)), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } } } diff --git a/alloc/src/collections/btree/map.rs b/alloc/src/collections/btree/map.rs index 766f4589177a8..426be364a56b0 100644 --- a/alloc/src/collections/btree/map.rs +++ b/alloc/src/collections/btree/map.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use core::error::Error; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; @@ -1181,6 +1181,10 @@ impl BTreeMap { /// /// If a key from `other` is already present in `self`, the respective /// value from `self` will be overwritten with the respective value from `other`. + /// Similar to [`insert`], though, the key is not overwritten, + /// which matters for types that can be `==` without being identical. + /// + /// [`insert`]: BTreeMap::insert /// /// # Examples /// @@ -1624,6 +1628,9 @@ impl ExactSizeIterator for Iter<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, K, V> {} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Iter<'_, K, V> { fn clone(&self) -> Self { @@ -1696,6 +1703,9 @@ impl ExactSizeIterator for IterMut<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterMut<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IterMut<'_, K, V> {} @@ -1817,6 +1827,9 @@ impl ExactSizeIterator for IntoIter { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} @@ -1865,6 +1878,9 @@ impl ExactSizeIterator for Keys<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Keys<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Keys<'_, K, V> {} @@ -1920,6 +1936,9 @@ impl ExactSizeIterator for Values<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Values<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Values<'_, K, V> {} @@ -2160,6 +2179,9 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ValuesMut<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} @@ -2222,6 +2244,9 @@ impl ExactSizeIterator for IntoKeys { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoKeys {} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoKeys {} @@ -2273,6 +2298,9 @@ impl ExactSizeIterator for IntoValues { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoValues {} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoValues {} @@ -2416,7 +2444,7 @@ impl Default for BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for BTreeMap { fn eq(&self, other: &BTreeMap) -> bool { - self.iter().eq(other) + self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) } } diff --git a/alloc/src/collections/btree/map/tests.rs b/alloc/src/collections/btree/map/tests.rs index 79879d31d3dfd..a61a2da172d1e 100644 --- a/alloc/src/collections/btree/map/tests.rs +++ b/alloc/src/collections/btree/map/tests.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; use crate::rc::Rc; use crate::string::{String, ToString}; use crate::testing::crash_test::{CrashTestDummy, Panic}; -use crate::testing::ord_chaos::{Cyclic3, Governed, Governor}; +use crate::testing::ord_chaos::{Cyclic3, Governed, Governor, IdBased}; use crate::testing::rng::DeterministicRng; // Minimum number of elements to insert, to guarantee a tree with 2 levels, @@ -2137,9 +2137,9 @@ fn test_append_drop_leak() { let mut left = BTreeMap::new(); let mut right = BTreeMap::new(); left.insert(a.spawn(Panic::Never), ()); - left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append + left.insert(b.spawn(Panic::Never), ()); left.insert(c.spawn(Panic::Never), ()); - right.insert(b.spawn(Panic::Never), ()); + right.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append right.insert(c.spawn(Panic::Never), ()); catch_unwind(move || left.append(&mut right)).unwrap_err(); @@ -2587,3 +2587,31 @@ fn cursor_peek_prev_agrees_with_cursor_mut() { let prev = cursor.peek_prev(); assert_matches!(prev, Some((&3, _))); } + +#[test] +fn test_id_based_insert() { + let mut lhs = BTreeMap::new(); + let mut rhs = BTreeMap::new(); + + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string()); + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string()); + + for (k, v) in rhs.into_iter() { + lhs.insert(k, v); + } + + assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); +} + +#[test] +fn test_id_based_append() { + let mut lhs = BTreeMap::new(); + let mut rhs = BTreeMap::new(); + + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string()); + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string()); + + lhs.append(&mut rhs); + + assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); +} diff --git a/alloc/src/collections/btree/set.rs b/alloc/src/collections/btree/set.rs index 28d26699d7d2c..fd27e87b1f470 100644 --- a/alloc/src/collections/btree/set.rs +++ b/alloc/src/collections/btree/set.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering::{self, Equal, Greater, Less}; use core::cmp::{max, min}; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use core::iter::{FusedIterator, Peekable}; +use core::iter::{FusedIterator, Peekable, TrustedLen}; use core::mem::ManuallyDrop; use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; @@ -1753,6 +1753,7 @@ impl Clone for Iter<'_, T> { Iter { iter: self.iter.clone() } } } + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; @@ -1783,12 +1784,14 @@ impl<'a, T> Iterator for Iter<'a, T> { self.next_back() } } + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> DoubleEndedIterator for Iter<'a, T> { fn next_back(&mut self) -> Option<&'a T> { self.iter.next_back() } } + #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Iter<'_, T> { fn len(&self) -> usize { @@ -1796,6 +1799,9 @@ impl ExactSizeIterator for Iter<'_, T> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, T> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Iter<'_, T> {} @@ -1832,6 +1838,7 @@ impl DoubleEndedIterator for IntoIter { self.iter.next_back().map(|(k, _)| k) } } + #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { @@ -1839,6 +1846,9 @@ impl ExactSizeIterator for IntoIter { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} diff --git a/alloc/src/lib.rs b/alloc/src/lib.rs index 3f391fe2c1de8..bf73deb0e8372 100644 --- a/alloc/src/lib.rs +++ b/alloc/src/lib.rs @@ -127,7 +127,6 @@ #![feature(layout_for_ptr)] #![feature(legacy_receiver_trait)] #![feature(local_waker)] -#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] diff --git a/alloctests/lib.rs b/alloctests/lib.rs index 73c25679d05ba..f6c7105ea2801 100644 --- a/alloctests/lib.rs +++ b/alloctests/lib.rs @@ -28,7 +28,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] -#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(ptr_alignment_type)] #![feature(ptr_internals)] diff --git a/alloctests/testing/ord_chaos.rs b/alloctests/testing/ord_chaos.rs index 55e1ae5e3deaa..f90ba1c69921e 100644 --- a/alloctests/testing/ord_chaos.rs +++ b/alloctests/testing/ord_chaos.rs @@ -2,6 +2,8 @@ use std::cell::Cell; use std::cmp::Ordering::{self, *}; use std::ptr; +use crate::string::String; + // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] pub(crate) enum Cyclic3 { @@ -79,3 +81,31 @@ impl PartialEq for Governed<'_, T> { } impl Eq for Governed<'_, T> {} + +// Comparison based only on the ID, the name is ignored. +#[derive(Debug)] +pub(crate) struct IdBased { + pub id: u32, + #[allow(dead_code)] + pub name: String, +} + +impl PartialEq for IdBased { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for IdBased {} + +impl PartialOrd for IdBased { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for IdBased { + fn cmp(&self, other: &Self) -> Ordering { + self.id.cmp(&other.id) + } +} diff --git a/alloctests/tests/collections/eq_diff_len.rs b/alloctests/tests/collections/eq_diff_len.rs new file mode 100644 index 0000000000000..ee1e294d37c67 --- /dev/null +++ b/alloctests/tests/collections/eq_diff_len.rs @@ -0,0 +1,96 @@ +//! Regression tests which fail if some collections' `PartialEq::eq` impls compare +//! elements when the collections have different sizes. +//! This behavior is not guaranteed either way, so regressing these tests is fine +//! if it is done on purpose. +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList}; + +/// This intentionally has a panicking `PartialEq` impl, to test that various +/// collections' `PartialEq` impls don't actually compare elements if their sizes +/// are unequal. +/// +/// This is not advisable in normal code. +#[derive(Debug, Clone, Copy, Hash)] +struct Evil; + +impl PartialEq for Evil { + fn eq(&self, _: &Self) -> bool { + panic!("Evil::eq is evil"); + } +} +impl Eq for Evil {} + +impl PartialOrd for Evil { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } +} + +impl Ord for Evil { + fn cmp(&self, _: &Self) -> Ordering { + // Constructing a `BTreeSet`/`BTreeMap` uses `cmp` on the elements, + // but comparing it with with `==` uses `eq` on the elements, + // so Evil::cmp doesn't need to be evil. + Ordering::Equal + } +} + +// check Evil works +#[test] +#[should_panic = "Evil::eq is evil"] +fn evil_eq_works() { + let v1 = vec![Evil]; + let v2 = vec![Evil]; + + _ = v1 == v2; +} + +// check various containers don't compare if their sizes are different + +#[test] +fn vec_evil_eq() { + let v1 = vec![Evil]; + let v2 = vec![Evil; 2]; + + assert_eq!(false, v1 == v2); +} + +#[test] +fn hashset_evil_eq() { + let s1 = HashSet::from([(0, Evil)]); + let s2 = HashSet::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, s1 == s2); +} + +#[test] +fn hashmap_evil_eq() { + let m1 = HashMap::from([(0, Evil)]); + let m2 = HashMap::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, m1 == m2); +} + +#[test] +fn btreeset_evil_eq() { + let s1 = BTreeSet::from([(0, Evil)]); + let s2 = BTreeSet::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, s1 == s2); +} + +#[test] +fn btreemap_evil_eq() { + let m1 = BTreeMap::from([(0, Evil)]); + let m2 = BTreeMap::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, m1 == m2); +} + +#[test] +fn linkedlist_evil_eq() { + let m1 = LinkedList::from([Evil]); + let m2 = LinkedList::from([Evil; 2]); + + assert_eq!(false, m1 == m2); +} diff --git a/alloctests/tests/collections/mod.rs b/alloctests/tests/collections/mod.rs index e73f3aaef8c83..2d387f0e77eb5 100644 --- a/alloctests/tests/collections/mod.rs +++ b/alloctests/tests/collections/mod.rs @@ -1 +1,2 @@ mod binary_heap; +mod eq_diff_len; diff --git a/core/src/alloc/global.rs b/core/src/alloc/global.rs index e2413b619f9fa..6447fde0cd8a9 100644 --- a/core/src/alloc/global.rs +++ b/core/src/alloc/global.rs @@ -115,6 +115,31 @@ use crate::{cmp, ptr}; /// Whether allocations happen or not is not part of the program behavior, even if it /// could be detected via an allocator that tracks allocations by printing or otherwise /// having side effects. +/// +/// # Re-entrance +/// +/// When implementing a global allocator one has to be careful not to create an infinitely recursive +/// implementation by accident, as many constructs in the Rust standard library may allocate in +/// their implementation. For example, on some platforms [`std::sync::Mutex`] may allocate, so using +/// it is highly problematic in a global allocator. +/// +/// Generally speaking for this reason one should stick to library features available through +/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are +/// guaranteed to not use `#[global_allocator]` to allocate: +/// +/// - [`std::thread_local`], +/// - [`std::thread::current`], +/// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and +/// [`Clone`] implementation. +/// +/// [`std`]: ../../std/index.html +/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html +/// [`std::thread_local`]: ../../std/macro.thread_local.html +/// [`std::thread::current`]: ../../std/thread/fn.current.html +/// [`std::thread::park`]: ../../std/thread/fn.park.html +/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html +/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark + #[stable(feature = "global_alloc", since = "1.28.0")] pub unsafe trait GlobalAlloc { /// Allocates memory as described by the given `layout`. diff --git a/core/src/array/drain.rs b/core/src/array/drain.rs index 5fadf907b6219..1c6137191324c 100644 --- a/core/src/array/drain.rs +++ b/core/src/array/drain.rs @@ -1,76 +1,108 @@ -use crate::iter::{TrustedLen, UncheckedIterator}; -use crate::mem::ManuallyDrop; -use crate::ptr::drop_in_place; -use crate::slice; +use crate::marker::{Destruct, PhantomData}; +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; -/// A situationally-optimized version of `array.into_iter().for_each(func)`. -/// -/// [`crate::array::IntoIter`]s are great when you need an owned iterator, but -/// storing the entire array *inside* the iterator like that can sometimes -/// pessimize code. Notable, it can be more bytes than you really want to move -/// around, and because the array accesses index into it SRoA has a harder time -/// optimizing away the type than it does iterators that just hold a couple pointers. -/// -/// Thus this function exists, which gives a way to get *moved* access to the -/// elements of an array using a small iterator -- no bigger than a slice iterator. -/// -/// The function-taking-a-closure structure makes it safe, as it keeps callers -/// from looking at already-dropped elements. -pub(crate) fn drain_array_with( - array: [T; N], - func: impl for<'a> FnOnce(Drain<'a, T>) -> R, -) -> R { - let mut array = ManuallyDrop::new(array); - // SAFETY: Now that the local won't drop it, it's ok to construct the `Drain` which will. - let drain = Drain(array.iter_mut()); - func(drain) +impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { + /// This function returns a function that lets you index the given array in const. + /// As implemented it can optimize better than iterators, and can be constified. + /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented + /// as it is a struct that implements const fn; + /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. + /// The only method you're really allowed to call is `next()`, + /// anything else is more or less UB, hence this function being unsafe. + /// Moved elements will not be dropped. + /// This will also not actually store the array. + /// + /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. + // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { + // dont drop the array, transfers "ownership" to Self + let ptr: NonNull = NonNull::from_mut(array).cast(); + // SAFETY: + // Adding `slice.len()` to the starting pointer gives a pointer + // at the end of `slice`. `end` will never be dereferenced, only checked + // for direct pointer equality with `ptr` to check if the drainer is done. + unsafe { + let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; + Self { ptr, end, f, l: PhantomData } + } + } } -/// See [`drain_array_with`] -- this is `pub(crate)` only so it's allowed to be -/// mentioned in the signature of that method. (Otherwise it hits `E0446`.) -// INVARIANT: It's ok to drop the remainder of the inner iterator. -pub(crate) struct Drain<'a, T>(slice::IterMut<'a, T>); +/// See [`Drain::new`]; this is our fake iterator. +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +pub(super) struct Drain<'l, 'f, T, const N: usize, F> { + // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. + /// The pointer to the next element to return, or the past-the-end location + /// if the drainer is empty. + /// + /// This address will be used for all ZST elements, never changed. + /// As we "own" this array, we dont need to store any lifetime. + ptr: NonNull, + /// For non-ZSTs, the non-null pointer to the past-the-end element. + /// For ZSTs, this is null. + end: *mut T, -impl Drop for Drain<'_, T> { - fn drop(&mut self) { - // SAFETY: By the type invariant, we're allowed to drop all these. - unsafe { drop_in_place(self.0.as_mut_slice()) } - } + f: &'f mut F, + l: PhantomData<&'l mut [T; N]>, } -impl Iterator for Drain<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - let p: *const T = self.0.next()?; - // SAFETY: The iterator was already advanced, so we won't drop this later. - Some(unsafe { p.read() }) - } +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> +where + F: [const] FnMut(T) -> U, +{ + type Output = U; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.len(); - (n, Some(n)) + /// This implementation is useless. + extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { + self.call_mut(args) } } - -impl ExactSizeIterator for Drain<'_, T> { - #[inline] - fn len(&self) -> usize { - self.0.len() +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> +where + F: [const] FnMut(T) -> U, +{ + // FIXME(const-hack): ideally this would be an unsafe fn `next()`, and to use it you would instead `|_| unsafe { drain.next() }`. + extern "rust-call" fn call_mut( + &mut self, + (_ /* ignore argument */,): (usize,), + ) -> Self::Output { + if T::IS_ZST { + // its UB to call this more than N times, so returning more ZSTs is valid. + // SAFETY: its a ZST? we conjur. + (self.f)(unsafe { conjure_zst::() }) + } else { + // increment before moving; if `f` panics, we drop the rest. + let p = self.ptr; + // SAFETY: caller guarantees never called more than N times (see `Drain::new`) + self.ptr = unsafe { self.ptr.add(1) }; + // SAFETY: we are allowed to move this. + (self.f)(unsafe { p.read() }) + } } } +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const Drop for Drain<'_, '_, T, N, F> { + fn drop(&mut self) { + if !T::IS_ZST { + // SAFETY: we cant read more than N elements + let slice = unsafe { + from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + // SAFETY: `start <= end` + self.end.offset_from_unsigned(self.ptr.as_ptr()), + ) + }; -// SAFETY: This is a 1:1 wrapper for a slice iterator, which is also `TrustedLen`. -unsafe impl TrustedLen for Drain<'_, T> {} - -impl UncheckedIterator for Drain<'_, T> { - unsafe fn next_unchecked(&mut self) -> T { - // SAFETY: `Drain` is 1:1 with the inner iterator, so if the caller promised - // that there's an element left, the inner iterator has one too. - let p: *const T = unsafe { self.0.next_unchecked() }; - // SAFETY: The iterator was already advanced, so we won't drop this later. - unsafe { p.read() } + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) + unsafe { drop_in_place(slice) } + } } } diff --git a/core/src/array/mod.rs b/core/src/array/mod.rs index 2dd639d68f0ea..c8d0622059cde 100644 --- a/core/src/array/mod.rs +++ b/core/src/array/mod.rs @@ -12,7 +12,8 @@ use crate::error::Error; use crate::hash::{self, Hash}; use crate::intrinsics::transmute_unchecked; use crate::iter::{UncheckedIterator, repeat_n}; -use crate::mem::{self, MaybeUninit}; +use crate::marker::Destruct; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, }; @@ -25,7 +26,6 @@ mod drain; mod equality; mod iter; -pub(crate) use drain::drain_array_with; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; @@ -105,9 +105,10 @@ pub fn repeat(val: T) -> [T; N] { /// ``` #[inline] #[stable(feature = "array_from_fn", since = "1.63.0")] -pub fn from_fn(f: F) -> [T; N] +#[rustc_const_unstable(feature = "const_array", issue = "147606")] +pub const fn from_fn(f: F) -> [T; N] where - F: FnMut(usize) -> T, + F: [const] FnMut(usize) -> T + [const] Destruct, { try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -143,11 +144,11 @@ where /// ``` #[inline] #[unstable(feature = "array_try_from_fn", issue = "89379")] -pub fn try_from_fn(cb: F) -> ChangeOutputType +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +pub const fn try_from_fn(cb: F) -> ChangeOutputType where - F: FnMut(usize) -> R, - R: Try, - R::Residual: Residual<[R::Output; N]>, + R: [const] Try, Output: [const] Destruct>, + F: [const] FnMut(usize) -> R + [const] Destruct, { let mut array = [const { MaybeUninit::uninit() }; N]; match try_from_fn_erased(&mut array, cb) { @@ -549,9 +550,12 @@ impl [T; N] { /// ``` #[must_use] #[stable(feature = "array_map", since = "1.55.0")] - pub fn map(self, f: F) -> [U; N] + #[rustc_const_unstable(feature = "const_array", issue = "147606")] + pub const fn map(self, f: F) -> [U; N] where - F: FnMut(T) -> U, + F: [const] FnMut(T) -> U + [const] Destruct, + U: [const] Destruct, + T: [const] Destruct, { self.try_map(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -587,11 +591,19 @@ impl [T; N] { /// assert_eq!(c, Some(a)); /// ``` #[unstable(feature = "array_try_map", issue = "79711")] - pub fn try_map(self, f: impl FnMut(T) -> R) -> ChangeOutputType + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub const fn try_map( + self, + mut f: impl [const] FnMut(T) -> R + [const] Destruct, + ) -> ChangeOutputType where - R: Try>, + R: [const] Try, Output: [const] Destruct>, + T: [const] Destruct, { - drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) + let mut me = ManuallyDrop::new(self); + // SAFETY: try_from_fn calls `f` N times. + let mut f = unsafe { drain::Drain::new(&mut me, &mut f) }; + try_from_fn(&mut f) } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. @@ -885,13 +897,11 @@ where /// not optimizing away. So if you give it a shot, make sure to watch what /// happens in the codegen tests. #[inline] -fn try_from_fn_erased( - buffer: &mut [MaybeUninit], - mut generator: impl FnMut(usize) -> R, -) -> ControlFlow -where - R: Try, -{ +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +const fn try_from_fn_erased>( + buffer: &mut [MaybeUninit], + mut generator: impl [const] FnMut(usize) -> R + [const] Destruct, +) -> ControlFlow { let mut guard = Guard { array_mut: buffer, initialized: 0 }; while guard.initialized < guard.array_mut.len() { @@ -930,7 +940,8 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] + pub(crate) const unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times then writes will be in-bounds // and slots will not be initialized more than once. @@ -941,11 +952,11 @@ impl Guard<'_, T> { } } -impl Drop for Guard<'_, T> { +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +impl const Drop for Guard<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); - // SAFETY: this slice will contain only initialized objects. unsafe { self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); diff --git a/core/src/clone/uninit.rs b/core/src/clone/uninit.rs index 8d1185067eb88..b6e351fc7c96c 100644 --- a/core/src/clone/uninit.rs +++ b/core/src/clone/uninit.rs @@ -114,16 +114,10 @@ impl<'a, T> InitializingSlice<'a, T> { impl<'a, T> Drop for InitializingSlice<'a, T> { #[cold] // will only be invoked on unwind fn drop(&mut self) { - let initialized_slice = ptr::slice_from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(self.data), - self.initialized_len, - ); // SAFETY: // * the pointer is valid because it was made from a mutable reference // * `initialized_len` counts the initialized elements as an invariant of this type, // so each of the pointed-to elements is initialized and may be dropped. - unsafe { - ptr::drop_in_place::<[T]>(initialized_slice); - } + unsafe { self.data[..self.initialized_len].assume_init_drop() }; } } diff --git a/core/src/fmt/mod.rs b/core/src/fmt/mod.rs index 4b1e013c2b445..7a80023ce64eb 100644 --- a/core/src/fmt/mod.rs +++ b/core/src/fmt/mod.rs @@ -625,7 +625,7 @@ impl<'a> Formatter<'a> { // // 2) Placeholders representation (e.g. format_args!("hello {name}\n")) // ┌────────────────────────────────┐ -// template: │ *const u8 │ ─▷ b"\x06hello \x80\x01\n\x00" +// template: │ *const u8 │ ─▷ b"\x06hello \xC0\x01\n\x00" // ├────────────────────────────────┤ // args: │ &'a [Argument<'a>; _] 0│ (lower bit is 0 due to alignment of Argument type) // └────────────────────────────────┘ diff --git a/core/src/intrinsics/mod.rs b/core/src/intrinsics/mod.rs index 9d99b05b38d69..2115c5c9a85d8 100644 --- a/core/src/intrinsics/mod.rs +++ b/core/src/intrinsics/mod.rs @@ -2947,61 +2947,77 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); #[rustc_intrinsic] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); -/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. +/// Returns the minimum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::min`] +/// The stabilized version of this intrinsic is [`f16::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf16(x: f16, y: f16) -> f16; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f32` values. +/// Returns the minimum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::min`] +/// The stabilized version of this intrinsic is [`f32::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf32(x: f32, y: f32) -> f32; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f64` values. +/// Returns the minimum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::min`] +/// The stabilized version of this intrinsic is [`f64::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf64(x: f64, y: f64) -> f64; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f128` values. +/// Returns the minimum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 minNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::min`] +/// The stabilized version of this intrinsic is [`f128::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf128(x: f128, y: f128) -> f128; -/// Returns the minimum (IEEE 754-2019 minimum) of two `f16` values. +/// Returns the minimum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3022,7 +3038,11 @@ pub const fn minimumf16(x: f16, y: f16) -> f16 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f32` values. +/// Returns the minimum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3043,7 +3063,11 @@ pub const fn minimumf32(x: f32, y: f32) -> f32 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f64` values. +/// Returns the minimum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3064,7 +3088,11 @@ pub const fn minimumf64(x: f64, y: f64) -> f64 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f128` values. +/// Returns the minimum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3085,61 +3113,77 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 { } } -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f16` values. +/// Returns the maximum of two `f16` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::max`] +/// The stabilized version of this intrinsic is [`f16::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf16(x: f16, y: f16) -> f16; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f32` values. +/// Returns the maximum of two `f32` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::max`] +/// The stabilized version of this intrinsic is [`f32::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf32(x: f32, y: f32) -> f32; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f64` values. +/// Returns the maximum of two `f64` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::max`] +/// The stabilized version of this intrinsic is [`f64::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf64(x: f64, y: f64) -> f64; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f128` values. +/// Returns the maximum of two `f128` values, ignoring NaN. +/// +/// This behaves like IEEE 754-2008 maxNum. In particular: +/// If one of the arguments is NaN, then the other argument is returned. If the inputs compare equal +/// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::max`] +/// The stabilized version of this intrinsic is [`f128::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf128(x: f128, y: f128) -> f128; -/// Returns the maximum (IEEE 754-2019 maximum) of two `f16` values. +/// Returns the maximum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3159,7 +3203,11 @@ pub const fn maximumf16(x: f16, y: f16) -> f16 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f32` values. +/// Returns the maximum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3179,7 +3227,11 @@ pub const fn maximumf32(x: f32, y: f32) -> f32 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f64` values. +/// Returns the maximum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3199,7 +3251,11 @@ pub const fn maximumf64(x: f64, y: f64) -> f64 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f128` values. +/// Returns the maximum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. diff --git a/core/src/iter/traits/iterator.rs b/core/src/iter/traits/iterator.rs index 29230b1665380..f898382086512 100644 --- a/core/src/iter/traits/iterator.rs +++ b/core/src/iter/traits/iterator.rs @@ -4034,6 +4034,62 @@ pub trait Iterator { { unreachable!("Always specialized"); } + + /// Checks if the iterator contains *exactly* one element. + /// If so, returns this one element. + /// + /// See also [`collect_array`](Iterator::collect_array) for lengths other than `1`. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_length_collection)] + /// + /// assert_eq!([1].into_iter().exactly_one(), Some(1)); + /// assert_eq!([].into_iter().exactly_one(), None::<()>); + /// + /// // There is exactly one even integer in the array: + /// assert_eq!([1, 2, 3].into_iter().filter(|x| x % 2 == 0).exactly_one(), Some(2)); + /// // But there are two odds, which is too many: + /// assert_eq!([1, 2, 3].into_iter().filter(|x| x % 2 == 1).exactly_one(), None); + /// ``` + #[inline] + #[unstable(feature = "exact_length_collection", issue = "149266")] + fn exactly_one(self) -> Option + where + Self: Sized, + { + self.collect_array::<1>().map(|[i]| i) + } + + /// Checks if an iterator has *exactly* `N` elements. + /// If so, returns those `N` elements in an array. + /// + /// See also [`exactly_one`](Iterator::exactly_one) when expecting a single element. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_length_collection)] + /// + /// assert_eq!([1, 2, 3, 4].into_iter().collect_array(), Some([1, 2, 3, 4])); + /// assert_eq!([1, 2].into_iter().chain([3, 4]).collect_array(), Some([1, 2, 3, 4])); + /// + /// // Iterator contains too few elements: + /// assert_eq!([1, 2].into_iter().collect_array::<4>(), None); + /// // Iterator contains too many elements: + /// assert_eq!([1, 2, 3, 4, 5].into_iter().collect_array::<4>(), None); + /// // Taking 4 makes it work again: + /// assert_eq!([1, 2, 3, 4, 5].into_iter().take(4).collect_array(), Some([1, 2, 3, 4])); + /// ``` + #[inline] + #[unstable(feature = "exact_length_collection", issue = "149266")] + fn collect_array(mut self) -> Option<[Self::Item; N]> + where + Self: Sized, + { + self.next_chunk().ok().filter(|_| self.next().is_none()) + } } trait SpecIterEq: Iterator { diff --git a/core/src/lib.rs b/core/src/lib.rs index ef85e36900869..9ecc2365e69b7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -126,8 +126,6 @@ #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] #![feature(ub_checks)] -#![feature(unchecked_neg)] -#![feature(unchecked_shifts)] #![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] @@ -149,6 +147,7 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(derive_const)] +#![feature(diagnostic_on_const)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/core/src/mem/drop_guard.rs b/core/src/mem/drop_guard.rs index fecc94b815e97..1301dedf1241a 100644 --- a/core/src/mem/drop_guard.rs +++ b/core/src/mem/drop_guard.rs @@ -63,13 +63,10 @@ where /// Consumes the `DropGuard`, returning the wrapped value. /// - /// This will not execute the closure. This is implemented as an associated - /// function to prevent any potential conflicts with any other methods called - /// `into_inner` from the `Deref` and `DerefMut` impls. - /// - /// It is typically preferred to call this function instead of `mem::forget` - /// because it will return the stored value and drop variables captured - /// by the closure instead of leaking their owned resources. + /// This will not execute the closure. It is typically preferred to call + /// this function instead of `mem::forget` because it will return the stored + /// value and drop variables captured by the closure instead of leaking their + /// owned resources. /// /// # Example /// @@ -81,26 +78,26 @@ where /// /// let value = String::from("Nori likes chicken"); /// let guard = DropGuard::new(value, |s| println!("{s}")); - /// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken"); + /// assert_eq!(guard.dismiss(), "Nori likes chicken"); /// ``` #[unstable(feature = "drop_guard", issue = "144426")] #[inline] - pub fn into_inner(guard: Self) -> T { + pub fn dismiss(self) -> T { // First we ensure that dropping the guard will not trigger // its destructor - let mut guard = ManuallyDrop::new(guard); + let mut this = ManuallyDrop::new(self); // Next we manually read the stored value from the guard. // // SAFETY: this is safe because we've taken ownership of the guard. - let value = unsafe { ManuallyDrop::take(&mut guard.inner) }; + let value = unsafe { ManuallyDrop::take(&mut this.inner) }; // Finally we drop the stored closure. We do this *after* having read // the value, so that even if the closure's `drop` function panics, // unwinding still tries to drop the value. // // SAFETY: this is safe because we've taken ownership of the guard. - unsafe { ManuallyDrop::drop(&mut guard.f) }; + unsafe { ManuallyDrop::drop(&mut this.f) }; value } } diff --git a/core/src/mem/maybe_uninit.rs b/core/src/mem/maybe_uninit.rs index 3507d1a0a9a8c..e00cf45fcab20 100644 --- a/core/src/mem/maybe_uninit.rs +++ b/core/src/mem/maybe_uninit.rs @@ -1047,7 +1047,7 @@ impl MaybeUninit { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let val = 0x12345678_i32; @@ -1097,20 +1097,6 @@ impl MaybeUninit { ) } } - - /// Gets a pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { - this.as_ptr() as *const T - } - - /// Gets a mutable pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { - this.as_mut_ptr() as *mut T - } } impl [MaybeUninit] { @@ -1128,7 +1114,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [MaybeUninit::uninit(); 32]; @@ -1140,8 +1125,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = [0; 16]; /// @@ -1157,7 +1140,8 @@ impl [MaybeUninit] { /// ``` /// /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy, @@ -1188,7 +1172,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [const { MaybeUninit::uninit() }; 5]; @@ -1203,8 +1186,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); /// @@ -1220,7 +1201,7 @@ impl [MaybeUninit] { /// ``` /// /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone, @@ -1415,7 +1396,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; @@ -1442,7 +1423,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; @@ -1482,7 +1463,7 @@ impl [MaybeUninit] { /// requirement the compiler knows about it is that the data pointer must be /// non-null. Dropping such a `Vec` however will cause undefined /// behaviour. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] pub const unsafe fn assume_init_drop(&mut self) @@ -1504,7 +1485,8 @@ impl [MaybeUninit] { /// Calling this when the content is not yet fully initialized causes undefined /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in /// the slice really is in an initialized state. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] pub const unsafe fn assume_init_ref(&self) -> &[T] { // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that @@ -1522,7 +1504,8 @@ impl [MaybeUninit] { /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot /// be used to initialize a `MaybeUninit` slice. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a diff --git a/core/src/num/f128.rs b/core/src/num/f128.rs index 2cf06b6d6a35a..9b9cc80a606c6 100644 --- a/core/src/num/f128.rs +++ b/core/src/num/f128.rs @@ -694,11 +694,14 @@ impl f128 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmax`. /// /// ``` /// #![feature(f128)] @@ -709,6 +712,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f128::NAN), x); /// # } /// ``` #[inline] @@ -721,11 +725,14 @@ impl f128 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmin`. /// /// ``` /// #![feature(f128)] @@ -736,6 +743,7 @@ impl f128 { /// let y = 2.0f128; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f128::NAN), x); /// # } /// ``` #[inline] @@ -767,7 +775,7 @@ impl f128 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `maximum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -800,7 +808,7 @@ impl f128 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `minimum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -1236,7 +1244,8 @@ impl f128 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1253,6 +1262,12 @@ impl f128 { /// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0); /// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f128).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f128).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f128).clamp(-0.0, 1.0).is_sign_negative()); /// # } /// ``` #[inline] @@ -1276,6 +1291,38 @@ impl f128 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(clamp_magnitude)] + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// assert_eq!(5.0f128.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f128).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f128.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f128).clamp_magnitude(3.0), -2.0); + /// # } + /// ``` + #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "this returns the clamped value and does not modify the original"] + pub fn clamp_magnitude(self, limit: f128) -> f128 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/core/src/num/f16.rs b/core/src/num/f16.rs index 51f803672e5c6..ab765ebcb7fa7 100644 --- a/core/src/num/f16.rs +++ b/core/src/num/f16.rs @@ -687,11 +687,14 @@ impl f16 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmax`. /// /// ``` /// #![feature(f16)] @@ -701,6 +704,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f16::NAN), x); /// # } /// ``` #[inline] @@ -713,11 +717,14 @@ impl f16 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmin`. /// /// ``` /// #![feature(f16)] @@ -727,6 +734,7 @@ impl f16 { /// let y = 2.0f16; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f16::NAN), x); /// # } /// ``` #[inline] @@ -757,7 +765,7 @@ impl f16 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `maximum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -789,7 +797,7 @@ impl f16 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `minimum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -1215,7 +1223,8 @@ impl f16 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1231,6 +1240,12 @@ impl f16 { /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0); /// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f16).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f16).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f16).clamp(-0.0, 1.0).is_sign_negative()); /// # } /// ``` #[inline] @@ -1254,6 +1269,38 @@ impl f16 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(clamp_magnitude)] + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// assert_eq!(5.0f16.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f16).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f16.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f16).clamp_magnitude(3.0), -2.0); + /// # } + /// ``` + #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "this returns the clamped value and does not modify the original"] + pub fn clamp_magnitude(self, limit: f16) -> f16 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/core/src/num/f32.rs b/core/src/num/f32.rs index 3070e1dedbe43..361f273ead019 100644 --- a/core/src/num/f32.rs +++ b/core/src/num/f32.rs @@ -897,17 +897,21 @@ impl f32 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmax`. /// /// ``` /// let x = 1.0f32; /// let y = 2.0f32; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -919,17 +923,21 @@ impl f32 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmin`. /// /// ``` /// let x = 1.0f32; /// let y = 2.0f32; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -955,7 +963,7 @@ impl f32 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `maximum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -982,7 +990,7 @@ impl f32 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `minimum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -1395,7 +1403,8 @@ impl f32 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1408,6 +1417,12 @@ impl f32 { /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f32).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f32).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f32).clamp(-0.0, 1.0).is_sign_negative()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] @@ -1431,6 +1446,35 @@ impl f32 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// assert_eq!(5.0f32.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f32).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0); + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: f32) -> f32 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/core/src/num/f64.rs b/core/src/num/f64.rs index dc8ccc551b2da..17a908643a41f 100644 --- a/core/src/num/f64.rs +++ b/core/src/num/f64.rs @@ -915,17 +915,21 @@ impl f64 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `maxNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `maxNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmax`. /// /// ``` /// let x = 1.0_f64; /// let y = 2.0_f64; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -937,17 +941,21 @@ impl f64 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN, then the other argument is returned. If both + /// arguments are NaN, the return value is NaN, with the bit pattern picked using the usual + /// [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs compare equal (such + /// as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// + /// This follows the IEEE 754-2008 semantics for `minNum`, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids `minNum`'s problems with associativity. + /// This also matches the behavior of libm’s `fmin`. /// /// ``` /// let x = 1.0_f64; /// let y = 2.0_f64; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -973,7 +981,7 @@ impl f64 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `maximum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -1000,7 +1008,7 @@ impl f64 { /// /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. + /// Note that this follows the IEEE 754-2019 semantics for `minimum`. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. @@ -1393,7 +1401,8 @@ impl f64 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1406,6 +1415,12 @@ impl f64 { /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f64).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f64).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f64).clamp(-0.0, 1.0).is_sign_negative()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] @@ -1429,6 +1444,35 @@ impl f64 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// assert_eq!(5.0f64.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f64).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0); + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: f64) -> f64 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. diff --git a/core/src/num/int_macros.rs b/core/src/num/int_macros.rs index 93fdf2823aeb7..99662768a29f2 100644 --- a/core/src/num/int_macros.rs +++ b/core/src/num/int_macros.rs @@ -372,6 +372,8 @@ macro_rules! int_impl { /// /// On big endian this is a no-op. On little endian the bytes are swapped. /// + /// See also [from_be_bytes()](Self::from_be_bytes). + /// /// # Examples /// /// ``` @@ -402,6 +404,8 @@ macro_rules! int_impl { /// /// On little endian this is a no-op. On big endian the bytes are swapped. /// + /// See also [from_le_bytes()](Self::from_le_bytes). + /// /// # Examples /// /// ``` @@ -428,9 +432,15 @@ macro_rules! int_impl { } } - /// Converts `self` to big endian from the target's endianness. + /// Swaps bytes of `self` on little endian targets. /// - /// On big endian this is a no-op. On little endian the bytes are swapped. + /// On big endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_be_bytes()`](Self::to_be_bytes) for a type-safe alternative. /// /// # Examples /// @@ -459,9 +469,15 @@ macro_rules! int_impl { } } - /// Converts `self` to little endian from the target's endianness. + /// Swaps bytes of `self` on big endian targets. /// - /// On little endian this is a no-op. On big endian the bytes are swapped. + /// On little endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_le_bytes()`](Self::to_le_bytes) for a type-safe alternative. /// /// # Examples /// @@ -1259,11 +1275,8 @@ macro_rules! int_impl { /// i.e. when [`checked_neg`] would return `None`. /// #[doc = concat!("[`checked_neg`]: ", stringify!($SelfT), "::checked_neg")] - #[unstable( - feature = "unchecked_neg", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_neg", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_neg", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1379,11 +1392,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1554,11 +1564,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -3871,5 +3878,32 @@ macro_rules! int_impl { pub const fn max_value() -> Self { Self::MAX } + + /// Clamps this number to a symmetric range centred around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + #[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")] + #[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")] + #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] + #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self { + if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { + self.clamp(-limit, limit) + } else { + self + } + } } } diff --git a/core/src/num/uint_macros.rs b/core/src/num/uint_macros.rs index 1d108cb0cf4a9..c8224e92b17e4 100644 --- a/core/src/num/uint_macros.rs +++ b/core/src/num/uint_macros.rs @@ -479,6 +479,76 @@ macro_rules! uint_impl { intrinsics::bswap(self as $ActualT) as Self } + /// Returns an integer with the bit locations specified by `mask` packed + /// contiguously into the least significant bits of the result. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")] + /// + /// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011); + /// assert_eq!(n.gather_bits(0xF0), 0b0000_1011); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn gather_bits(self, mut mask: Self) -> Self { + let mut bit_position = 1; + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration. We fill the bits in the result starting from the + // least significant bit. + while mask != 0 { + // Find the next lowest set bit in the mask + let next_mask_bit = mask.isolate_lowest_one(); + + // Retrieve the masked bit and if present, set it in the result + let src_bit = (self & next_mask_bit) != 0; + result |= if src_bit { bit_position } else { 0 }; + + // Unset lowest set bit in the mask, prepare next position to set + mask ^= next_mask_bit; + bit_position <<= 1; + } + + result + } + + /// Returns an integer with the least significant bits of `self` + /// distributed to the bit locations specified by `mask`. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")] + /// + /// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001); + /// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn scatter_bits(mut self, mut mask: Self) -> Self { + let mut result = 0; + + // Iterate through the mask bits, unsetting the lowest bit after + // each iteration and right-shifting `self` by one to get the next + // bit into the least significant bit position. + while mask != 0 { + // Find the next bit position to potentially set + let next_mask_bit = mask.isolate_lowest_one(); + + // If bit is set, deposit it at the masked bit position + result |= if (self & 1) != 0 { next_mask_bit } else { 0 }; + + // Unset lowest set bit in the mask, shift in next `self` bit + mask ^= next_mask_bit; + self >>= 1; + } + + result + } + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, /// second least-significant bit becomes second most-significant bit, etc. /// @@ -1781,11 +1851,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1953,11 +2020,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] diff --git a/core/src/ops/try_trait.rs b/core/src/ops/try_trait.rs index 204291886589e..34000f6d6b218 100644 --- a/core/src/ops/try_trait.rs +++ b/core/src/ops/try_trait.rs @@ -1,3 +1,4 @@ +use crate::marker::{Destruct, PhantomData}; use crate::ops::ControlFlow; /// The `?` operator and `try {}` blocks. @@ -363,6 +364,7 @@ where pub const trait Residual: Sized { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] + // FIXME: ought to be implied type TryType: [const] Try; } @@ -396,6 +398,25 @@ pub(crate) type ChangeOutputType>, V> = /// Not currently planned to be exposed publicly, so just `pub(crate)`. #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); +// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added. +pub(crate) struct Wrapped T> { + f: F, + p: PhantomData<(T, A)>, +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T + [const] Destruct> const FnOnce<(A,)> for Wrapped { + type Output = NeverShortCircuit; + + extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output { + self.call_mut(args) + } +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T> const FnMut<(A,)> for Wrapped { + extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output { + NeverShortCircuit((self.f)(args)) + } +} impl NeverShortCircuit { /// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`. @@ -403,10 +424,11 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub(crate) fn wrap_mut_1( - mut f: impl FnMut(A) -> T, - ) -> impl FnMut(A) -> NeverShortCircuit { - move |a| NeverShortCircuit(f(a)) + pub(crate) const fn wrap_mut_1(f: F) -> Wrapped + where + F: [const] FnMut(A) -> T, + { + Wrapped { f, p: PhantomData } } #[inline] @@ -417,7 +439,8 @@ impl NeverShortCircuit { pub(crate) enum NeverShortCircuitResidual {} -impl Try for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Try for NeverShortCircuit { type Output = T; type Residual = NeverShortCircuitResidual; @@ -431,15 +454,15 @@ impl Try for NeverShortCircuit { NeverShortCircuit(x) } } - -impl FromResidual for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const FromResidual for NeverShortCircuit { #[inline] fn from_residual(never: NeverShortCircuitResidual) -> Self { match never {} } } - -impl Residual for NeverShortCircuitResidual { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Residual for NeverShortCircuitResidual { type TryType = NeverShortCircuit; } diff --git a/core/src/ptr/const_ptr.rs b/core/src/ptr/const_ptr.rs index 84a6982d56805..2860bf0a6e69a 100644 --- a/core/src/ptr/const_ptr.rs +++ b/core/src/ptr/const_ptr.rs @@ -1567,6 +1567,10 @@ impl *const [T; N] { /// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1577,10 +1581,18 @@ impl PartialEq for *const T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *const T {} /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1597,6 +1609,10 @@ impl Ord for *const T { /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/core/src/ptr/mod.rs b/core/src/ptr/mod.rs index ea0514f405f1e..29fe77390a16b 100644 --- a/core/src/ptr/mod.rs +++ b/core/src/ptr/mod.rs @@ -2520,6 +2520,10 @@ pub fn hash(hashee: *const T, into: &mut S) { } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for F { #[inline] fn eq(&self, other: &Self) -> bool { @@ -2527,9 +2531,17 @@ impl PartialEq for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for F {} #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for F { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -2537,6 +2549,10 @@ impl PartialOrd for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for F { #[inline] fn cmp(&self, other: &Self) -> Ordering { diff --git a/core/src/ptr/mut_ptr.rs b/core/src/ptr/mut_ptr.rs index 85d54b4d3b9b3..f8dc6ef7ed71f 100644 --- a/core/src/ptr/mut_ptr.rs +++ b/core/src/ptr/mut_ptr.rs @@ -2000,6 +2000,10 @@ impl *mut [T; N] { /// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2010,10 +2014,18 @@ impl PartialEq for *mut T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *mut T {} /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *mut T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2030,6 +2042,10 @@ impl Ord for *mut T { /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/core/src/slice/iter.rs b/core/src/slice/iter.rs index 7053ae86e732f..a2fbf6ead6461 100644 --- a/core/src/slice/iter.rs +++ b/core/src/slice/iter.rs @@ -139,7 +139,7 @@ impl<'a, T> Iter<'a, T> { } } -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, { +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, each_ref, { fn is_sorted_by(self, mut compare: F) -> bool where Self: Sized, @@ -368,7 +368,7 @@ impl AsRef<[T]> for IterMut<'_, T> { // } // } -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}} +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, each_mut, {}} /// An internal abstraction over the splitting iterators, so that /// splitn, splitn_mut etc can be implemented once. diff --git a/core/src/slice/iter/macros.rs b/core/src/slice/iter/macros.rs index c46b7c797aab6..236bdf9d89cae 100644 --- a/core/src/slice/iter/macros.rs +++ b/core/src/slice/iter/macros.rs @@ -68,6 +68,7 @@ macro_rules! iterator { $raw_mut:tt, {$( $mut_:tt )?}, $into_ref:ident, + $array_ref:ident, {$($extra:tt)*} ) => { impl<'a, T> $name<'a, T> { @@ -190,6 +191,29 @@ macro_rules! iterator { } } + fn next_chunk(&mut self) -> Result<[$elem; N], crate::array::IntoIter<$elem, N>> { + if T::IS_ZST { + return crate::array::iter_next_chunk(self); + } + let len = len!(self); + if len >= N { + // SAFETY: we are just getting an array of [T; N] and moving the pointer over a little + let r = unsafe { self.post_inc_start(N).cast_array().$into_ref() } + .$array_ref(); // must convert &[T; N] to [&T; N] + Ok(r) + } else { + // cant use $array_ref because theres no builtin for &mut [MU; N] -> [&mut MU; N] + // cant use copy_nonoverlapping as the $elem is of type &{mut} T instead of T + let mut a = [const { crate::mem::MaybeUninit::<$elem>::uninit() }; N]; + for into in (&mut a).into_iter().take(len) { + // SAFETY: take(n) limits to remainder (slice produces worse codegen) + into.write(unsafe { self.post_inc_start(1).$into_ref() }); + } + // SAFETY: we just initialized elements 0..len + unsafe { Err(crate::array::IntoIter::new_unchecked(a, 0..len)) } + } + } + #[inline] fn size_hint(&self) -> (usize, Option) { let exact = len!(self); diff --git a/core/src/slice/sort/stable/merge.rs b/core/src/slice/sort/stable/merge.rs index bb2747bfc78ac..26d8480b7f71f 100644 --- a/core/src/slice/sort/stable/merge.rs +++ b/core/src/slice/sort/stable/merge.rs @@ -35,7 +35,7 @@ pub fn merge bool>( // 1. Protects integrity of `v` from panics in `is_less`. // 2. Fills the remaining gap in `v` if the longer run gets consumed first. - let buf = MaybeUninit::slice_as_mut_ptr(scratch); + let buf = scratch.as_mut_ptr().cast_init(); let v_base = v.as_mut_ptr(); let v_mid = v_base.add(mid); diff --git a/core/src/slice/sort/stable/quicksort.rs b/core/src/slice/sort/stable/quicksort.rs index 0439ba870bd2b..734a495ce225b 100644 --- a/core/src/slice/sort/stable/quicksort.rs +++ b/core/src/slice/sort/stable/quicksort.rs @@ -97,7 +97,7 @@ fn stable_partition bool>( } let v_base = v.as_ptr(); - let scratch_base = MaybeUninit::slice_as_mut_ptr(scratch); + let scratch_base = scratch.as_mut_ptr().cast_init(); // The core idea is to write the values that compare as less-than to the left // side of `scratch`, while the values that compared as greater or equal than diff --git a/core/src/sync/atomic.rs b/core/src/sync/atomic.rs index 0c5552a0b81cc..0601019abbd5c 100644 --- a/core/src/sync/atomic.rs +++ b/core/src/sync/atomic.rs @@ -130,16 +130,18 @@ //! //! * PowerPC and MIPS platforms with 32-bit pointers do not have `AtomicU64` or //! `AtomicI64` types. -//! * ARM platforms like `armv5te` that aren't for Linux only provide `load` -//! and `store` operations, and do not support Compare and Swap (CAS) -//! operations, such as `swap`, `fetch_add`, etc. Additionally on Linux, -//! these CAS operations are implemented via [operating system support], which -//! may come with a performance penalty. -//! * ARM targets with `thumbv6m` only provide `load` and `store` operations, -//! and do not support Compare and Swap (CAS) operations, such as `swap`, -//! `fetch_add`, etc. +//! * Legacy ARM platforms like ARMv4T and ARMv5TE have very limited hardware +//! support for atomics. The bare-metal targets disable this module +//! entirely, but the Linux targets [use the kernel] to assist (which comes +//! with a performance penalty). It's not until ARMv6K onwards that ARM CPUs +//! have support for load/store and Compare and Swap (CAS) atomics in hardware. +//! * ARMv6-M and ARMv8-M baseline targets (`thumbv6m-*` and +//! `thumbv8m.base-*`) only provide `load` and `store` operations, and do +//! not support Compare and Swap (CAS) operations, such as `swap`, +//! `fetch_add`, etc. Full CAS support is available on ARMv7-M and ARMv8-M +//! Mainline (`thumbv7m-*`, `thumbv7em*` and `thumbv8m.main-*`). //! -//! [operating system support]: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt +//! [use the kernel]: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt //! //! Note that future platforms may be added that also do not have support for //! some atomic operations. Maximally portable code will want to be careful diff --git a/coretests/tests/array.rs b/coretests/tests/array.rs index c4a8fc74feca3..2b4429092e98b 100644 --- a/coretests/tests/array.rs +++ b/coretests/tests/array.rs @@ -724,3 +724,20 @@ fn array_eq() { let not_true = [0u8] == [].as_slice(); assert!(!not_true); } + +#[test] +fn const_array_ops() { + const fn doubler(x: usize) -> usize { + x * 2 + } + const fn maybe_doubler(x: usize) -> Option { + x.checked_mul(2) + } + assert_eq!(const { std::array::from_fn::<_, 5, _>(doubler) }, [0, 2, 4, 6, 8]); + assert_eq!(const { [5, 6, 1, 2].map(doubler) }, [10, 12, 2, 4]); + assert_eq!(const { [1, usize::MAX, 2, 8].try_map(maybe_doubler) }, None); + assert_eq!(const { std::array::try_from_fn::<_, 5, _>(maybe_doubler) }, Some([0, 2, 4, 6, 8])); + #[derive(Debug, PartialEq)] + struct Zst; + assert_eq!([(); 10].try_map(|()| Some(Zst)), Some([const { Zst }; 10])); +} diff --git a/coretests/tests/lib.rs b/coretests/tests/lib.rs index e190536abcf9f..eb2a2161cc28d 100644 --- a/coretests/tests/lib.rs +++ b/coretests/tests/lib.rs @@ -4,6 +4,7 @@ #![feature(alloc_layout_extra)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] +#![feature(array_try_map)] #![feature(array_windows)] #![feature(ascii_char)] #![feature(ascii_char_variants)] @@ -15,7 +16,9 @@ #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] #![feature(char_max_len)] +#![feature(clamp_magnitude)] #![feature(clone_to_uninit)] +#![feature(const_array)] #![feature(const_cell_traits)] #![feature(const_cmp)] #![feature(const_convert)] @@ -81,7 +84,6 @@ #![feature(lazy_get)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] #![feature(new_range_api)] @@ -115,6 +117,7 @@ #![feature(try_find)] #![feature(try_trait_v2)] #![feature(uint_bit_width)] +#![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/coretests/tests/mem.rs b/coretests/tests/mem.rs index e896c61ef4881..00582109aa2c5 100644 --- a/coretests/tests/mem.rs +++ b/coretests/tests/mem.rs @@ -815,7 +815,7 @@ fn drop_guard_into_inner() { let dropped = Cell::new(false); let value = DropGuard::new(42, |_| dropped.set(true)); let guard = DropGuard::new(value, |_| dropped.set(true)); - let inner = DropGuard::into_inner(guard); + let inner = guard.dismiss(); assert_eq!(dropped.get(), false); assert_eq!(*inner, 42); } @@ -837,7 +837,7 @@ fn drop_guard_always_drops_value_if_closure_drop_unwinds() { // run the destructor of the value we passed, which we validate. let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop); - DropGuard::into_inner(guard); + guard.dismiss(); })); assert!(value_was_dropped); } diff --git a/coretests/tests/num/clamp_magnitude.rs b/coretests/tests/num/clamp_magnitude.rs new file mode 100644 index 0000000000000..0f96e55f6914e --- /dev/null +++ b/coretests/tests/num/clamp_magnitude.rs @@ -0,0 +1,139 @@ +macro_rules! check_int_clamp { + ($t:ty, $ut:ty) => { + let min = <$t>::MIN; + let max = <$t>::MAX; + let max_u = <$ut>::MAX; + + // Basic clamping + assert_eq!((100 as $t).clamp_magnitude(50), 50); + assert_eq!((-100 as $t).clamp_magnitude(50), -50); + assert_eq!((30 as $t).clamp_magnitude(50), 30); + assert_eq!((-30 as $t).clamp_magnitude(50), -30); + + // Exact boundary + assert_eq!((50 as $t).clamp_magnitude(50), 50); + assert_eq!((-50 as $t).clamp_magnitude(50), -50); + + // Zero cases + assert_eq!((0 as $t).clamp_magnitude(100), 0); + assert_eq!((0 as $t).clamp_magnitude(0), 0); + assert_eq!((100 as $t).clamp_magnitude(0), 0); + assert_eq!((-100 as $t).clamp_magnitude(0), 0); + + // MIN/MAX values + // Symmetric range [-MAX, MAX] + assert_eq!(max.clamp_magnitude(max as $ut), max); + assert_eq!(min.clamp_magnitude(max as $ut), -max); + + // Full range (limit covers MIN) + let min_abs = min.unsigned_abs(); + assert_eq!(min.clamp_magnitude(min_abs), min); + + // Limit larger than type max (uN > iN::MAX) + assert_eq!(max.clamp_magnitude(max_u), max); + assert_eq!(min.clamp_magnitude(max_u), min); + }; +} + +#[test] +fn test_clamp_magnitude_i8() { + check_int_clamp!(i8, u8); +} + +#[test] +fn test_clamp_magnitude_i16() { + check_int_clamp!(i16, u16); +} + +#[test] +fn test_clamp_magnitude_i32() { + check_int_clamp!(i32, u32); +} + +#[test] +fn test_clamp_magnitude_i64() { + check_int_clamp!(i64, u64); +} + +#[test] +fn test_clamp_magnitude_i128() { + check_int_clamp!(i128, u128); +} + +#[test] +fn test_clamp_magnitude_isize() { + check_int_clamp!(isize, usize); +} + +macro_rules! check_float_clamp { + ($t:ty) => { + // Basic clamping + assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0); + assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0); + assert_eq!((2.0 as $t).clamp_magnitude(3.0), 2.0); + assert_eq!((-2.0 as $t).clamp_magnitude(3.0), -2.0); + + // Exact boundary + assert_eq!((3.0 as $t).clamp_magnitude(3.0), 3.0); + assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0); + + // Zero cases + assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0); + assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0); + + // Special values - Infinity + let inf = <$t>::INFINITY; + let neg_inf = <$t>::NEG_INFINITY; + assert_eq!(inf.clamp_magnitude(100.0), 100.0); + assert_eq!(neg_inf.clamp_magnitude(100.0), -100.0); + assert_eq!(inf.clamp_magnitude(inf), inf); + + // Value with infinite limit + assert_eq!((1.0 as $t).clamp_magnitude(inf), 1.0); + assert_eq!((-1.0 as $t).clamp_magnitude(inf), -1.0); + + // MIN and MAX + let max = <$t>::MAX; + let min = <$t>::MIN; + // Large limit + let huge = 1e30; + assert_eq!(max.clamp_magnitude(huge), huge); + assert_eq!(min.clamp_magnitude(huge), -huge); + }; +} + +#[test] +fn test_clamp_magnitude_f32() { + check_float_clamp!(f32); +} + +#[test] +fn test_clamp_magnitude_f64() { + check_float_clamp!(f64); +} + +#[test] +#[should_panic(expected = "limit must be non-negative")] +fn test_clamp_magnitude_f32_panic_negative_limit() { + let _ = 1.0f32.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic(expected = "limit must be non-negative")] +fn test_clamp_magnitude_f64_panic_negative_limit() { + let _ = 1.0f64.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic] +fn test_clamp_magnitude_f32_panic_nan_limit() { + let _ = 1.0f32.clamp_magnitude(f32::NAN); +} + +#[test] +#[should_panic] +fn test_clamp_magnitude_f64_panic_nan_limit() { + let _ = 1.0f64.clamp_magnitude(f64::NAN); +} diff --git a/coretests/tests/num/uint_macros.rs b/coretests/tests/num/uint_macros.rs index b89a371efcc25..7f3e27e9c446c 100644 --- a/coretests/tests/num/uint_macros.rs +++ b/coretests/tests/num/uint_macros.rs @@ -127,6 +127,52 @@ macro_rules! uint_module { assert_eq_const_safe!($T: _1.swap_bytes(), _1); } + fn test_gather_bits() { + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001); + assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010); + + assert_eq_const_safe!($T: A.gather_bits(_0), 0); + assert_eq_const_safe!($T: B.gather_bits(_0), 0); + assert_eq_const_safe!($T: C.gather_bits(_0), 0); + assert_eq_const_safe!($T: _0.gather_bits(A), 0); + assert_eq_const_safe!($T: _0.gather_bits(B), 0); + assert_eq_const_safe!($T: _0.gather_bits(C), 0); + + assert_eq_const_safe!($T: A.gather_bits(_1), A); + assert_eq_const_safe!($T: B.gather_bits(_1), B); + assert_eq_const_safe!($T: C.gather_bits(_1), C); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011); + assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111); + assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111); + } + + fn test_scatter_bits() { + assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110); + assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010); + assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100); + assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000); + assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000); + + assert_eq_const_safe!($T: A.scatter_bits(_0), 0); + assert_eq_const_safe!($T: B.scatter_bits(_0), 0); + assert_eq_const_safe!($T: C.scatter_bits(_0), 0); + assert_eq_const_safe!($T: _0.scatter_bits(A), 0); + assert_eq_const_safe!($T: _0.scatter_bits(B), 0); + assert_eq_const_safe!($T: _0.scatter_bits(C), 0); + + assert_eq_const_safe!($T: A.scatter_bits(_1), A); + assert_eq_const_safe!($T: B.scatter_bits(_1), B); + assert_eq_const_safe!($T: C.scatter_bits(_1), C); + assert_eq_const_safe!($T: _1.scatter_bits(A), A); + assert_eq_const_safe!($T: _1.scatter_bits(B), B); + assert_eq_const_safe!($T: _1.scatter_bits(C), C); + } + fn test_reverse_bits() { assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A); assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B); diff --git a/proc_macro/Cargo.toml b/proc_macro/Cargo.toml index 0042a6e8ece58..3a4840a57334b 100644 --- a/proc_macro/Cargo.toml +++ b/proc_macro/Cargo.toml @@ -14,3 +14,6 @@ rustc-literal-escaper = { version = "0.0.5", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] rustc-dep-of-std = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } diff --git a/proc_macro/src/lib.rs b/proc_macro/src/lib.rs index 4efdfcad924b5..108ef7bce81a2 100644 --- a/proc_macro/src/lib.rs +++ b/proc_macro/src/lib.rs @@ -22,7 +22,7 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(maybe_uninit_write_slice)] +#![cfg_attr(bootstrap, feature(maybe_uninit_write_slice))] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(restricted_std)] diff --git a/std/src/alloc.rs b/std/src/alloc.rs index daa25c5a50dd6..9254aa55507e7 100644 --- a/std/src/alloc.rs +++ b/std/src/alloc.rs @@ -11,7 +11,7 @@ //! //! This attribute allows configuring the choice of global allocator. //! You can use this to implement a completely custom global allocator -//! to route all default allocation requests to a custom object. +//! to route all[^system-alloc] default allocation requests to a custom object. //! //! ```rust //! use std::alloc::{GlobalAlloc, System, Layout}; @@ -52,12 +52,19 @@ //! //! The `#[global_allocator]` can only be used once in a crate //! or its recursive dependencies. +//! +//! [^system-alloc]: Note that the Rust standard library internals may still +//! directly call [`System`] when necessary (for example for the runtime +//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`] +//! for more details). +//! +//! [re-entrance]: trait.GlobalAlloc.html#re-entrance #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] use core::ptr::NonNull; -use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] @@ -287,7 +294,7 @@ unsafe impl Allocator for System { } } -static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. /// @@ -344,7 +351,12 @@ pub fn take_alloc_error_hook() -> fn(Layout) { if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } +#[optimize(size)] fn default_alloc_error_hook(layout: Layout) { + if cfg!(panic = "immediate-abort") { + return; + } + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. @@ -354,16 +366,65 @@ fn default_alloc_error_hook(layout: Layout) { if unsafe { __rust_alloc_error_handler_should_panic_v2() != 0 } { panic!("memory allocation of {} bytes failed", layout.size()); + } + + // This is the default path taken on OOM, and the only path taken on stable with std. + // Crucially, it does *not* call any user-defined code, and therefore users do not have to + // worry about allocation failure causing reentrancy issues. That makes it different from + // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error + // handler that is called when there is no `#[alloc_error_handler]`), which triggers a + // regular panic and thus can invoke a user-defined panic hook, executing arbitrary + // user-defined code. + + static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false); + if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) { + // Don't try to print a backtrace if a previous alloc error happened. This likely means + // there is not enough memory to print a backtrace, although it could also mean that two + // threads concurrently run out of memory. + rtprintpanic!( + "memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n", + layout.size() + ); + return; } else { - // This is the default path taken on OOM, and the only path taken on stable with std. - // Crucially, it does *not* call any user-defined code, and therefore users do not have to - // worry about allocation failure causing reentrancy issues. That makes it different from - // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error - // handler that is called when there is no `#[alloc_error_handler]`), which triggers a - // regular panic and thus can invoke a user-defined panic hook, executing arbitrary - // user-defined code. rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); } + + let Some(mut out) = crate::sys::stdio::panic_output() else { + return; + }; + + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. + // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks + // when there is too little memory to print a backtrace. + let mut lock = crate::sys::backtrace::lock(); + + match crate::panic::get_backtrace_style() { + Some(crate::panic::BacktraceStyle::Short) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short)) + } + Some(crate::panic::BacktraceStyle::Full) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full)) + } + Some(crate::panic::BacktraceStyle::Off) => { + use crate::io::Write; + let _ = writeln!( + out, + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ + backtrace" + ); + if cfg!(miri) { + let _ = writeln!( + out, + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ + for the environment variable to have an effect" + ); + } + } + // If backtraces aren't supported or are forced-off, do nothing. + None => {} + } } #[cfg(not(test))] @@ -371,11 +432,13 @@ fn default_alloc_error_hook(layout: Layout) { #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::Acquire); - let hook: fn(Layout) = - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; - hook(layout); - crate::process::abort() + crate::sys::backtrace::__rust_end_short_backtrace(|| { + let hook = HOOK.load(Ordering::Acquire); + let hook: fn(Layout) = + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; + hook(layout); + crate::process::abort() + }) } #[cfg(not(test))] diff --git a/std/src/lib.rs b/std/src/lib.rs index 32f166e112e99..0401e9b39ff49 100644 --- a/std/src/lib.rs +++ b/std/src/lib.rs @@ -265,8 +265,6 @@ // // Language features: // tidy-alphabetical-start - -// stabilization was reverted after it hit beta #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -348,8 +346,6 @@ #![feature(ip)] #![feature(lazy_get)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -422,7 +418,7 @@ #![default_lib_allocator] // The Rust prelude -// The compiler expects the prelude definition to be defined before it's use statement. +// The compiler expects the prelude definition to be defined before its use statement. pub mod prelude; // Explicitly import the prelude. The compiler uses this same unstable attribute diff --git a/std/src/panicking.rs b/std/src/panicking.rs index 7efb7ad8ee8b3..a4a974d0447b8 100644 --- a/std/src/panicking.rs +++ b/std/src/panicking.rs @@ -285,7 +285,6 @@ fn default_hook(info: &PanicHookInfo<'_>) { static FIRST_PANIC: Atomic = AtomicBool::new(true); match backtrace { - // SAFETY: we took out a lock just a second ago. Some(BacktraceStyle::Short) => { drop(lock.print(err, crate::backtrace_rs::PrintFmt::Short)) } diff --git a/std/src/process.rs b/std/src/process.rs index 5c0ac526a36c9..dbcf2684c6fb1 100644 --- a/std/src/process.rs +++ b/std/src/process.rs @@ -1206,6 +1206,30 @@ impl Command { pub fn get_current_dir(&self) -> Option<&Path> { self.inner.get_current_dir() } + + /// Returns whether the environment will be cleared for the child process. + /// + /// This returns `true` if [`Command::env_clear`] was called, and `false` otherwise. + /// When `true`, the child process will not inherit any environment variables from + /// its parent process. + /// + /// # Examples + /// + /// ``` + /// #![feature(command_resolved_envs)] + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_env_clear(), false); + /// + /// cmd.env_clear(); + /// assert_eq!(cmd.get_env_clear(), true); + /// ``` + #[must_use] + #[unstable(feature = "command_resolved_envs", issue = "149070")] + pub fn get_env_clear(&self) -> bool { + self.inner.get_env_clear() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/std/src/sync/barrier.rs b/std/src/sync/barrier.rs index c2c18889dde7d..6a5cc9b69f82c 100644 --- a/std/src/sync/barrier.rs +++ b/std/src/sync/barrier.rs @@ -65,8 +65,8 @@ impl fmt::Debug for Barrier { impl Barrier { /// Creates a new barrier that can block a given number of threads. /// - /// A barrier will block `n`-1 threads which call [`wait()`] and then wake - /// up all threads at once when the `n`th thread calls [`wait()`]. + /// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`], + /// and then wake up all threads at once. /// /// [`wait()`]: Barrier::wait /// diff --git a/std/src/sys/backtrace.rs b/std/src/sys/backtrace.rs index 57682207e078e..8e4e6aab0e49a 100644 --- a/std/src/sys/backtrace.rs +++ b/std/src/sys/backtrace.rs @@ -20,8 +20,6 @@ pub(crate) fn lock<'a>() -> BacktraceLock<'a> { impl BacktraceLock<'_> { /// Prints the current backtrace. - /// - /// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { // There are issues currently linking libbacktrace into tests, and in // general during std's own unit tests we're not testing this path. In @@ -36,6 +34,7 @@ impl BacktraceLock<'_> { } impl fmt::Display for DisplayBacktrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: the backtrace lock is held unsafe { _print_fmt(fmt, self.format) } } } @@ -43,6 +42,9 @@ impl BacktraceLock<'_> { } } +/// # Safety +/// +/// This function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { // Always 'fail' to get the cwd when running under Miri - // this allows Miri to display backtraces in isolation mode diff --git a/std/src/sys/fs/uefi.rs b/std/src/sys/fs/uefi.rs index 7625409007a46..bd4ae56974f0c 100644 --- a/std/src/sys/fs/uefi.rs +++ b/std/src/sys/fs/uefi.rs @@ -344,8 +344,16 @@ pub fn readdir(_p: &Path) -> io::Result { unsupported() } -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() +pub fn unlink(p: &Path) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); + + if file_attr.file_type().is_file() { + f.delete() + } else { + Err(io::const_error!(io::ErrorKind::IsADirectory, "expected a file but got a directory")) + } } pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { @@ -364,8 +372,16 @@ pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { unsupported() } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(p: &Path) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); + + if file_attr.file_type().is_dir() { + f.delete() + } else { + Err(io::const_error!(io::ErrorKind::NotADirectory, "expected a directory but got a file")) + } } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -537,6 +553,16 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(info) } } + + pub(crate) fn delete(self) -> io::Result<()> { + let file_ptr = self.0.as_ptr(); + let r = unsafe { ((*file_ptr).delete)(file_ptr) }; + + // Spec states that even in case of failure, the file handle will be closed. + crate::mem::forget(self); + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } } impl Drop for File { diff --git a/std/src/sys/pal/motor/mod.rs b/std/src/sys/pal/motor/mod.rs index c64f8ff7a8a83..32f95df6ad082 100644 --- a/std/src/sys/pal/motor/mod.rs +++ b/std/src/sys/pal/motor/mod.rs @@ -58,16 +58,27 @@ pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { } match code as moto_rt::ErrorCode /* u16 */ { - E_ALREADY_IN_USE => ErrorKind::AlreadyExists, - E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_UNSPECIFIED => ErrorKind::Uncategorized, + E_UNKNOWN => ErrorKind::Uncategorized, + E_NOT_READY => ErrorKind::WouldBlock, + E_NOT_IMPLEMENTED => ErrorKind::Unsupported, + E_VERSION_TOO_HIGH => ErrorKind::Unsupported, + E_VERSION_TOO_LOW => ErrorKind::Unsupported, + E_INVALID_ARGUMENT => ErrorKind::InvalidInput, + E_OUT_OF_MEMORY => ErrorKind::OutOfMemory, + E_NOT_ALLOWED => ErrorKind::PermissionDenied, E_NOT_FOUND => ErrorKind::NotFound, + E_INTERNAL_ERROR => ErrorKind::Other, E_TIMED_OUT => ErrorKind::TimedOut, - E_NOT_IMPLEMENTED => ErrorKind::Unsupported, - E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, + E_ALREADY_IN_USE => ErrorKind::AlreadyExists, E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, - E_INVALID_ARGUMENT => ErrorKind::InvalidInput, - E_NOT_READY => ErrorKind::WouldBlock, + E_INVALID_FILENAME => ErrorKind::InvalidFilename, + E_NOT_A_DIRECTORY => ErrorKind::NotADirectory, + E_BAD_HANDLE => ErrorKind::InvalidInput, + E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, E_NOT_CONNECTED => ErrorKind::NotConnected, + E_STORAGE_FULL => ErrorKind::StorageFull, + E_INVALID_DATA => ErrorKind::InvalidData, _ => crate::io::ErrorKind::Uncategorized, } } diff --git a/std/src/sys/pal/sgx/abi/mod.rs b/std/src/sys/pal/sgx/abi/mod.rs index b8c4d7740c4e1..1c6c681d4c179 100644 --- a/std/src/sys/pal/sgx/abi/mod.rs +++ b/std/src/sys/pal/sgx/abi/mod.rs @@ -3,6 +3,7 @@ use core::arch::global_asm; use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::alloc::System; use crate::io::Write; // runtime features @@ -63,7 +64,9 @@ unsafe extern "C" fn tcs_init(secondary: bool) { #[unsafe(no_mangle)] extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn { // FIXME: how to support TLS in library mode? - let tls = Box::new(tls::Tls::new()); + // We use the System allocator here such that the global allocator may use + // thread-locals. + let tls = Box::new_in(tls::Tls::new(), System); let tls_guard = unsafe { tls.activate() }; if secondary { diff --git a/std/src/sys/pal/sgx/abi/tls/mod.rs b/std/src/sys/pal/sgx/abi/tls/mod.rs index 41e38b6961680..553814dcb5fda 100644 --- a/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -89,13 +89,6 @@ impl Tls { ActiveTls { tls: self } } - #[allow(unused)] - pub unsafe fn activate_persistent(self: Box) { - // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. - let ptr = Box::into_raw(self).cast_const().cast::(); - unsafe { set_tls_ptr(ptr) }; - } - unsafe fn current<'a>() -> &'a Tls { // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. unsafe { &*(get_tls_ptr() as *const Tls) } diff --git a/std/src/sys/process/motor.rs b/std/src/sys/process/motor.rs index 9060902bc3d20..949a9d4942901 100644 --- a/std/src/sys/process/motor.rs +++ b/std/src/sys/process/motor.rs @@ -98,6 +98,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/std/src/sys/process/uefi.rs b/std/src/sys/process/uefi.rs index 11c8b682bb9bc..8d44292611bcb 100644 --- a/std/src/sys/process/uefi.rs +++ b/std/src/sys/process/uefi.rs @@ -83,6 +83,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { None } diff --git a/std/src/sys/process/unix/common.rs b/std/src/sys/process/unix/common.rs index 1d5909e99bacc..44d54aaf51512 100644 --- a/std/src/sys/process/unix/common.rs +++ b/std/src/sys/process/unix/common.rs @@ -263,6 +263,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes()))) } diff --git a/std/src/sys/process/unsupported.rs b/std/src/sys/process/unsupported.rs index 636465b68e541..2dfc676ec0059 100644 --- a/std/src/sys/process/unsupported.rs +++ b/std/src/sys/process/unsupported.rs @@ -86,6 +86,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(cs)) } diff --git a/std/src/sys/process/windows.rs b/std/src/sys/process/windows.rs index 7d58093c54bbf..6e8be21a1fa6d 100644 --- a/std/src/sys/process/windows.rs +++ b/std/src/sys/process/windows.rs @@ -250,6 +250,10 @@ impl Command { self.env.iter() } + pub fn get_env_clear(&self) -> bool { + self.env.does_clear() + } + pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(Path::new) } diff --git a/std/src/sys/thread/hermit.rs b/std/src/sys/thread/hermit.rs index 4d9f3b114c2a0..faeaa9ae2dfcc 100644 --- a/std/src/sys/thread/hermit.rs +++ b/std/src/sys/thread/hermit.rs @@ -1,4 +1,5 @@ use crate::num::NonZero; +use crate::thread::ThreadInit; use crate::time::Duration; use crate::{io, ptr}; @@ -16,14 +17,14 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20; impl Thread { pub unsafe fn new_with_coreid( stack: usize, - p: Box, + init: Box, core_id: isize, ) -> io::Result { - let p = Box::into_raw(Box::new(p)); + let data = Box::into_raw(init); let tid = unsafe { hermit_abi::spawn2( thread_start, - p.expose_provenance(), + data.expose_provenance(), hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), stack, core_id, @@ -31,35 +32,34 @@ impl Thread { }; return if tid == 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is + // The thread failed to start and as a result data was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. unsafe { - drop(Box::from_raw(p)); + drop(Box::from_raw(data)); } Err(io::const_error!(io::ErrorKind::Uncategorized, "unable to create thread!")) } else { Ok(Thread { tid }) }; - extern "C" fn thread_start(main: usize) { - unsafe { - // Finally, let's run some code. - Box::from_raw(ptr::with_exposed_provenance::>(main).cast_mut())(); + extern "C" fn thread_start(data: usize) { + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = + unsafe { Box::from_raw(ptr::with_exposed_provenance_mut::(data)) }; + let rust_start = init.init(); + rust_start(); - // run all destructors + // Run all destructors. + unsafe { crate::sys::thread_local::destructors::run(); - crate::rt::thread_cleanup(); } + crate::rt::thread_cleanup(); } } - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { + pub unsafe fn new(stack: usize, init: Box) -> io::Result { unsafe { - Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + Thread::new_with_coreid(stack, init, -1 /* = no specific core */) } } diff --git a/std/src/sys/thread/sgx.rs b/std/src/sys/thread/sgx.rs index f20ef7d86b9c7..9e6dcfa16713d 100644 --- a/std/src/sys/thread/sgx.rs +++ b/std/src/sys/thread/sgx.rs @@ -2,6 +2,7 @@ use crate::io; use crate::sys::pal::abi::{thread, usercalls}; +use crate::thread::ThreadInit; use crate::time::Duration; pub struct Thread(task_queue::JoinHandle); @@ -13,6 +14,7 @@ pub use self::task_queue::JoinNotifier; mod task_queue { use super::wait_notify; use crate::sync::{Mutex, MutexGuard}; + use crate::thread::ThreadInit; pub type JoinHandle = wait_notify::Waiter; @@ -25,19 +27,20 @@ mod task_queue { } pub(super) struct Task { - p: Box, + init: Box, done: JoinNotifier, } impl Task { - pub(super) fn new(p: Box) -> (Task, JoinHandle) { + pub(super) fn new(init: Box) -> (Task, JoinHandle) { let (done, recv) = wait_notify::new(); let done = JoinNotifier(Some(done)); - (Task { p, done }, recv) + (Task { init, done }, recv) } pub(super) fn run(self) -> JoinNotifier { - (self.p)(); + let rust_start = self.init.init(); + rust_start(); self.done } } @@ -93,14 +96,10 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { + pub unsafe fn new(_stack: usize, init: Box) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; - let (task, handle) = task_queue::Task::new(p); + let (task, handle) = task_queue::Task::new(init); queue_lock.push(task); Ok(Thread(handle)) } diff --git a/std/src/sys/thread/solid.rs b/std/src/sys/thread/solid.rs index 46a84faa80225..5953c0e7b6129 100644 --- a/std/src/sys/thread/solid.rs +++ b/std/src/sys/thread/solid.rs @@ -8,6 +8,7 @@ use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting}; use crate::sys::pal::itron::time::dur2reltims; use crate::sys::pal::itron::{abi, task}; +use crate::thread::ThreadInit; use crate::time::Duration; use crate::{hint, io}; @@ -27,9 +28,9 @@ unsafe impl Sync for Thread {} /// State data shared between a parent thread and child thread. It's dropped on /// a transition to one of the final states. struct ThreadInner { - /// This field is used on thread creation to pass a closure from + /// This field is used on thread creation to pass initialization data from /// `Thread::new` to the created task. - start: UnsafeCell>>, + init: UnsafeCell>>, /// A state machine. Each transition is annotated with `[...]` in the /// source code. @@ -65,7 +66,7 @@ struct ThreadInner { lifecycle: Atomic, } -// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by +// Safety: The only `!Sync` field, `ThreadInner::init`, is only touched by // the task represented by `ThreadInner`. unsafe impl Sync for ThreadInner {} @@ -84,13 +85,9 @@ impl Thread { /// # Safety /// /// See `thread::Builder::spawn_unchecked` for safety requirements. - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { + pub unsafe fn new(stack: usize, init: Box) -> io::Result { let inner = Box::new(ThreadInner { - start: UnsafeCell::new(ManuallyDrop::new(p)), + init: UnsafeCell::new(ManuallyDrop::new(init)), lifecycle: AtomicUsize::new(LIFECYCLE_INIT), }); @@ -100,10 +97,11 @@ impl Thread { let inner = unsafe { &*p_inner }; // Safety: Since `trampoline` is called only once for each - // `ThreadInner` and only `trampoline` touches `start`, - // `start` contains contents and is safe to mutably borrow. - let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) }; - p(); + // `ThreadInner` and only `trampoline` touches `init`, + // `init` contains contents and is safe to mutably borrow. + let init = unsafe { ManuallyDrop::take(&mut *inner.init.get()) }; + let rust_start = init.init(); + rust_start(); // Fix the current thread's state just in case, so that the // destructors won't abort diff --git a/std/src/sys/thread/teeos.rs b/std/src/sys/thread/teeos.rs index cad100395c9f8..5e71f757eaa4b 100644 --- a/std/src/sys/thread/teeos.rs +++ b/std/src/sys/thread/teeos.rs @@ -1,5 +1,6 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::os; +use crate::thread::ThreadInit; use crate::time::Duration; use crate::{cmp, io, ptr}; @@ -24,12 +25,8 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new(stack: usize, init: Box) -> io::Result { + let data = Box::into_raw(init); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); @@ -62,16 +59,16 @@ impl Thread { } }; - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, data as *mut _) }; + // Note: if the thread creation fails and this assert fails, then data will // be leaked. However, an alternative design could cause double-free // which is clearly worse. assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is + // The thread failed to start and as a result data was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(unsafe { Box::from_raw(p) }); + drop(unsafe { Box::from_raw(data) }); Err(io::Error::from_raw_os_error(ret)) } else { // The new thread will start running earliest after the next yield. @@ -80,15 +77,11 @@ impl Thread { Ok(Thread { id: native }) }; - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - // this is not necessary in TEE. - //let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } + extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = unsafe { Box::from_raw(data as *mut ThreadInit) }; + let rust_start = init.init(); + rust_start(); ptr::null_mut() } } diff --git a/std/src/sys/thread/unix.rs b/std/src/sys/thread/unix.rs index 9b26262bc80dc..d4c27097afd79 100644 --- a/std/src/sys/thread/unix.rs +++ b/std/src/sys/thread/unix.rs @@ -14,6 +14,7 @@ use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; +use crate::thread::{ThreadInit, current}; use crate::time::Duration; use crate::{cmp, io, ptr}; #[cfg(not(any( @@ -30,11 +31,6 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; #[cfg(any(target_os = "espidf", target_os = "nuttx"))] pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used -struct ThreadData { - name: Option>, - f: Box, -} - pub struct Thread { id: libc::pthread_t, } @@ -47,13 +43,8 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new( - stack: usize, - name: Option<&str>, - f: Box, - ) -> io::Result { - let data = Box::new(ThreadData { name: name.map(Box::from), f }); - + pub unsafe fn new(stack: usize, init: Box) -> io::Result { + let data = init; let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); let mut attr = DropGuard::new(&mut attr, |attr| { @@ -116,12 +107,16 @@ impl Thread { extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { unsafe { - let data = Box::from_raw(data as *mut ThreadData); - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(data.name); - // Finally, let's run some code. - (data.f)(); + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = Box::from_raw(data as *mut ThreadInit); + let rust_start = init.init(); + + // Set up our thread name and stack overflow handler which may get triggered + // if we run out of stack. + let thread = current(); + let _handler = stack_overflow::Handler::new(thread.name().map(Box::from)); + + rust_start(); } ptr::null_mut() } diff --git a/std/src/sys/thread/unsupported.rs b/std/src/sys/thread/unsupported.rs index a5001efa3b405..d633e83371eae 100644 --- a/std/src/sys/thread/unsupported.rs +++ b/std/src/sys/thread/unsupported.rs @@ -1,19 +1,22 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZero; +use crate::thread::ThreadInit; use crate::time::Duration; +// Silence dead code warnings for the otherwise unused ThreadInit::init() call. +#[expect(dead_code)] +fn dummy_init_call(init: Box) { + drop(init.init()); +} + pub struct Thread(!); pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { + pub unsafe fn new(_stack: usize, _init: Box) -> io::Result { Err(io::Error::UNSUPPORTED_PLATFORM) } diff --git a/std/src/sys/thread/wasip1.rs b/std/src/sys/thread/wasip1.rs index 83001fad49c81..9287a9c5485cc 100644 --- a/std/src/sys/thread/wasip1.rs +++ b/std/src/sys/thread/wasip1.rs @@ -7,6 +7,8 @@ use crate::mem; use crate::num::NonZero; #[cfg(target_feature = "atomics")] use crate::sys::os; +#[cfg(target_feature = "atomics")] +use crate::thread::ThreadInit; use crate::time::Duration; #[cfg(target_feature = "atomics")] use crate::{cmp, ptr}; @@ -73,12 +75,8 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; #[cfg(target_feature = "atomics")] impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new(stack: usize, init: Box) -> io::Result { + let data = Box::into_raw(init); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); @@ -100,28 +98,28 @@ impl Thread { } }; - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, data as *mut _) }; + // Note: if the thread creation fails and this assert fails, then data will // be leaked. However, an alternative design could cause double-free // which is clearly worse. assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is + // The thread failed to start and as a result data was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. unsafe { - drop(Box::from_raw(p)); + drop(Box::from_raw(data)); } Err(io::Error::from_raw_os_error(ret)) } else { Ok(Thread { id: native }) }; - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } + extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = unsafe { Box::from_raw(data as *mut ThreadInit) }; + let rust_start = init.init(); + rust_start(); ptr::null_mut() } } diff --git a/std/src/sys/thread/windows.rs b/std/src/sys/thread/windows.rs index a5640c51c4a5d..1ef496a20cfe4 100644 --- a/std/src/sys/thread/windows.rs +++ b/std/src/sys/thread/windows.rs @@ -8,6 +8,7 @@ use crate::sys::pal::time::WaitableTimer; use crate::sys::pal::{dur2timeout, to_u16s}; use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; +use crate::thread::ThreadInit; use crate::time::Duration; use crate::{io, ptr}; @@ -20,23 +21,19 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new(stack: usize, init: Box) -> io::Result { + let data = Box::into_raw(init); // CreateThread rounds up values for the stack size to the nearest page size (at least 4kb). // If a value of zero is given then the default stack size is used instead. // SAFETY: `thread_start` has the right ABI for a thread's entry point. - // `p` is simply passed through to the new thread without being touched. + // `data` is simply passed through to the new thread without being touched. let ret = unsafe { let ret = c::CreateThread( ptr::null_mut(), stack, Some(thread_start), - p as *mut _, + data as *mut _, c::STACK_SIZE_PARAM_IS_A_RESERVATION, ptr::null_mut(), ); @@ -45,19 +42,21 @@ impl Thread { return if let Ok(handle) = ret.try_into() { Ok(Thread { handle: Handle::from_inner(handle) }) } else { - // The thread failed to start and as a result p was not consumed. Therefore, it is + // The thread failed to start and as a result data was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - unsafe { drop(Box::from_raw(p)) }; + unsafe { drop(Box::from_raw(data)) }; Err(io::Error::last_os_error()) }; - unsafe extern "system" fn thread_start(main: *mut c_void) -> u32 { - // Next, reserve some stack space for if we otherwise run out of stack. + unsafe extern "system" fn thread_start(data: *mut c_void) -> u32 { + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = unsafe { Box::from_raw(data as *mut ThreadInit) }; + let rust_start = init.init(); + + // Reserve some stack space for if we otherwise run out of stack. stack_overflow::reserve_stack(); - // Finally, let's run some code. - // SAFETY: We are simply recreating the box that was leaked earlier. - // It's the responsibility of the one who call `Thread::new` to ensure this is safe to call here. - unsafe { Box::from_raw(main as *mut Box)() }; + + rust_start(); 0 } } diff --git a/std/src/sys/thread/xous.rs b/std/src/sys/thread/xous.rs index 133e15a0928c6..6c2cdfa4acddf 100644 --- a/std/src/sys/thread/xous.rs +++ b/std/src/sys/thread/xous.rs @@ -7,6 +7,7 @@ use crate::os::xous::ffi::{ map_memory, update_memory_flags, }; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; +use crate::thread::ThreadInit; use crate::time::Duration; pub struct Thread { @@ -19,12 +20,8 @@ pub const GUARD_PAGE_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - stack: usize, - _name: Option<&str>, - p: Box, - ) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new(stack: usize, init: Box) -> io::Result { + let data = Box::into_raw(init); let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); if (stack_size & 4095) != 0 { @@ -65,22 +62,32 @@ impl Thread { let tid = create_thread( thread_start as *mut usize, &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)], - p as usize, + data as usize, guard_page_pre, stack_size, 0, ) .map_err(|code| io::Error::from_raw_os_error(code as i32))?; + #[inline(never)] + fn rust_main_thread_not_inlined(init: Box) { + let rust_start = init.init(); + rust_start(); + } + extern "C" fn thread_start( - main: *mut usize, + data: *mut usize, guard_page_pre: usize, stack_size: usize, ) -> ! { - unsafe { - // Run the contents of the new thread. - Box::from_raw(main as *mut Box)(); - } + // SAFETY: we are simply recreating the box that was leaked earlier. + let init = unsafe { Box::from_raw(data as *mut ThreadInit) }; + + // Run the main thread with an inline(never) barrier to prevent + // dealloc calls from being reordered to after the TLS has been destroyed. + // See https://github.com/rust-lang/rust/pull/144465#pullrequestreview-3289729950 + // for more context. + run_main_thread_not_inlined(init); // Destroy TLS, which will free the TLS page and call the destructor for // any thread local storage (if any). diff --git a/std/src/sys/thread_local/destructors/list.rs b/std/src/sys/thread_local/destructors/list.rs index b9d5214c438d2..44e00c8a5ae59 100644 --- a/std/src/sys/thread_local/destructors/list.rs +++ b/std/src/sys/thread_local/destructors/list.rs @@ -1,19 +1,16 @@ +use crate::alloc::System; use crate::cell::RefCell; use crate::sys::thread_local::guard; #[thread_local] -static DTORS: RefCell> = RefCell::new(Vec::new()); +static DTORS: RefCell> = + RefCell::new(Vec::new_in(System)); pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { let Ok(mut dtors) = DTORS.try_borrow_mut() else { - // This point can only be reached if the global allocator calls this - // function again. - // FIXME: maybe use the system allocator instead? - rtabort!("the global allocator may not use TLS with destructors"); + rtabort!("the System allocator may not use TLS with destructors") }; - guard::enable(); - dtors.push((t, dtor)); } @@ -36,7 +33,7 @@ pub unsafe fn run() { } None => { // Free the list memory. - *dtors = Vec::new(); + *dtors = Vec::new_in(System); break; } } diff --git a/std/src/sys/thread_local/key/xous.rs b/std/src/sys/thread_local/key/xous.rs index a27cec5ca1a60..db83d2bf4a13a 100644 --- a/std/src/sys/thread_local/key/xous.rs +++ b/std/src/sys/thread_local/key/xous.rs @@ -38,6 +38,7 @@ use core::arch::asm; +use crate::alloc::System; use crate::mem::ManuallyDrop; use crate::os::xous::ffi::{MemoryFlags, map_memory, unmap_memory}; use crate::ptr; @@ -151,7 +152,10 @@ struct Node { } unsafe fn register_dtor(key: Key, dtor: Dtor) { - let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); + // We use the System allocator here to avoid interfering with a potential + // Global allocator using thread-local storage. + let mut node = + ManuallyDrop::new(Box::new_in(Node { key, dtor, next: ptr::null_mut() }, System)); #[allow(unused_unsafe)] let mut head = unsafe { DTORS.load(Acquire) }; @@ -182,6 +186,11 @@ pub unsafe fn destroy_tls() { }; } +// This is marked inline(never) to prevent dealloc calls from being reordered +// to after the TLS has been destroyed. +// See https://github.com/rust-lang/rust/pull/144465#pullrequestreview-3289729950 +// for more context. +#[inline(never)] unsafe fn run_dtors() { let mut any_run = true; diff --git a/std/src/sys/thread_local/os.rs b/std/src/sys/thread_local/os.rs index 9f7a29236e926..07b93a2cbbc34 100644 --- a/std/src/sys/thread_local/os.rs +++ b/std/src/sys/thread_local/os.rs @@ -1,6 +1,6 @@ use super::key::{Key, LazyKey, get, set}; use super::{abort_on_dtor_unwind, guard}; -use crate::alloc::{self, Layout}; +use crate::alloc::{self, GlobalAlloc, Layout, System}; use crate::cell::Cell; use crate::marker::PhantomData; use crate::mem::ManuallyDrop; @@ -113,17 +113,19 @@ pub const fn value_align() -> usize { crate::mem::align_of::>() } -/// Equivalent to `Box>`, but potentially over-aligned. -struct AlignedBox { +/// Equivalent to `Box, System>`, but potentially over-aligned. +struct AlignedSystemBox { ptr: NonNull>, } -impl AlignedBox { +impl AlignedSystemBox { #[inline] fn new(v: Value) -> Self { let layout = Layout::new::>().align_to(ALIGN).unwrap(); - let ptr: *mut Value = (unsafe { alloc::alloc(layout) }).cast(); + // We use the System allocator here to avoid interfering with a potential + // Global allocator using thread-local storage. + let ptr: *mut Value = (unsafe { System.alloc(layout) }).cast(); let Some(ptr) = NonNull::new(ptr) else { alloc::handle_alloc_error(layout); }; @@ -143,7 +145,7 @@ impl AlignedBox { } } -impl Deref for AlignedBox { +impl Deref for AlignedSystemBox { type Target = Value; #[inline] @@ -152,14 +154,14 @@ impl Deref for AlignedBox { } } -impl Drop for AlignedBox { +impl Drop for AlignedSystemBox { #[inline] fn drop(&mut self) { let layout = Layout::new::>().align_to(ALIGN).unwrap(); unsafe { let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place())); - alloc::dealloc(self.ptr.as_ptr().cast(), layout); + System.dealloc(self.ptr.as_ptr().cast(), layout); if let Err(payload) = unwind_result { resume_unwind(payload); } @@ -205,11 +207,11 @@ impl Storage { return ptr::null(); } - let value = AlignedBox::::new(Value { + let value = AlignedSystemBox::::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key, }); - let ptr = AlignedBox::into_raw(value); + let ptr = AlignedSystemBox::into_raw(value); // SAFETY: // * key came from a `LazyKey` and is thus correct. @@ -227,7 +229,7 @@ impl Storage { // initializer has already returned and the next scope only starts // after we return the pointer. Therefore, there can be no references // to the old value. - drop(unsafe { AlignedBox::::from_raw(old) }); + drop(unsafe { AlignedSystemBox::::from_raw(old) }); } // SAFETY: We just created this value above. @@ -246,7 +248,7 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) // Note that to prevent an infinite loop we reset it back to null right // before we return from the destructor ourselves. abort_on_dtor_unwind(|| { - let ptr = unsafe { AlignedBox::::from_raw(ptr as *mut Value) }; + let ptr = unsafe { AlignedSystemBox::::from_raw(ptr as *mut Value) }; let key = ptr.key; // SAFETY: `key` is the TLS key `ptr` was stored under. unsafe { set(key, ptr::without_provenance_mut(1)) }; diff --git a/std/src/thread/builder.rs b/std/src/thread/builder.rs new file mode 100644 index 0000000000000..f4abe074ab9d7 --- /dev/null +++ b/std/src/thread/builder.rs @@ -0,0 +1,267 @@ +use super::join_handle::JoinHandle; +use super::lifecycle::spawn_unchecked; +use crate::io; + +/// Thread factory, which can be used in order to configure the properties of +/// a new thread. +/// +/// Methods can be chained on it in order to configure it. +/// +/// The two configurations available are: +/// +/// - [`name`]: specifies an [associated name for the thread][naming-threads] +/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] +/// +/// The [`spawn`] method will take ownership of the builder and create an +/// [`io::Result`] to the thread handle with the given configuration. +/// +/// The [`thread::spawn`] free function uses a `Builder` with default +/// configuration and [`unwrap`]s its return value. +/// +/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want +/// to recover from a failure to launch a thread, indeed the free function will +/// panic where the `Builder` method will return a [`io::Result`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let handler = builder.spawn(|| { +/// // thread code +/// }).unwrap(); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// [`stack_size`]: Builder::stack_size +/// [`name`]: Builder::name +/// [`spawn`]: Builder::spawn +/// [`thread::spawn`]: super::spawn +/// [`io::Result`]: crate::io::Result +/// [`unwrap`]: crate::result::Result::unwrap +/// [naming-threads]: ./index.html#naming-threads +/// [stack-size]: ./index.html#stack-size +#[must_use = "must eventually spawn the thread"] +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Builder { + /// A name for the thread-to-be, for identification in panic messages + pub(super) name: Option, + /// The size of the stack for the spawned thread in bytes + pub(super) stack_size: Option, + /// Skip running and inheriting the thread spawn hooks + pub(super) no_hooks: bool, +} + +impl Builder { + /// Generates the base configuration for spawning a thread, from which + /// configuration methods can be chained. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()) + /// .stack_size(32 * 1024); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Builder { + Builder { name: None, stack_size: None, no_hooks: false } + } + + /// Names the thread-to-be. Currently the name is used for identification + /// only in panic messages. + /// + /// The name must not contain null bytes (`\0`). + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Sets the size of the stack (in bytes) for the new thread. + /// + /// The actual stack size may be greater than this value if + /// the platform specifies a minimal stack size. + /// + /// For more information about the stack size for threads, see + /// [this module-level documentation][stack-size]. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new().stack_size(32 * 1024); + /// ``` + /// + /// [stack-size]: ./index.html#stack-size + #[stable(feature = "rust1", since = "1.0.0")] + pub fn stack_size(mut self, size: usize) -> Builder { + self.stack_size = Some(size); + self + } + + /// Disables running and inheriting [spawn hooks]. + /// + /// Use this if the parent thread is in no way relevant for the child thread. + /// For example, when lazily spawning threads for a thread pool. + /// + /// [spawn hooks]: super::add_spawn_hook + #[unstable(feature = "thread_spawn_hook", issue = "132951")] + pub fn no_hooks(mut self) -> Builder { + self.no_hooks = true; + self + } + + /// Spawns a new thread by taking ownership of the `Builder`, and returns an + /// [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the spawned thread, including recovering its panics. + /// + /// For a more complete documentation see [`thread::spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// [`io::Result`]: crate::io::Result + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// // thread code + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [`thread::spawn`]: super::spawn + /// [`spawn`]: super::spawn + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn spawn(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + unsafe { self.spawn_unchecked(f) } + } + + /// Spawns a new thread without any lifetime restrictions by taking ownership + /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the spawned thread, including recovering its panics. + /// + /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], + /// except for the relaxed lifetime bounds, which render it unsafe. + /// For a more complete documentation see [`thread::spawn`]. + /// + /// # Errors + /// + /// Unlike the [`spawn`] free function, this method yields an + /// [`io::Result`] to capture any failure to create the thread at + /// the OS level. + /// + /// # Panics + /// + /// Panics if a thread name was set and it contained null bytes. + /// + /// # Safety + /// + /// The caller has to ensure that the spawned thread does not outlive any + /// references in the supplied thread closure and its return type. + /// This can be guaranteed in two ways: + /// + /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced + /// data is dropped + /// - use only types with `'static` lifetime bounds, i.e., those with no or only + /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] + /// and [`thread::spawn`] enforce this property statically) + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let x = 1; + /// let thread_x = &x; + /// + /// let handler = unsafe { + /// builder.spawn_unchecked(move || { + /// println!("x = {}", *thread_x); + /// }).unwrap() + /// }; + /// + /// // caller has to ensure `join()` is called, otherwise + /// // it is possible to access freed memory if `x` gets + /// // dropped before the thread closure is executed! + /// handler.join().unwrap(); + /// ``` + /// + /// [`io::Result`]: crate::io::Result + /// [`thread::spawn`]: super::spawn + /// [`spawn`]: super::spawn + #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send, + T: Send, + { + let Builder { name, stack_size, no_hooks } = self; + Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?)) + } +} diff --git a/std/src/thread/current.rs b/std/src/thread/current.rs index f00212bfcb617..508e35cefe88f 100644 --- a/std/src/thread/current.rs +++ b/std/src/thread/current.rs @@ -1,6 +1,9 @@ -use super::{Thread, ThreadId, imp}; +use super::id::ThreadId; +use super::main_thread; +use super::thread::Thread; use crate::mem::ManuallyDrop; use crate::ptr; +use crate::sys::thread as imp; use crate::sys::thread_local::local_pointer; const NONE: *mut () = ptr::null_mut(); @@ -184,7 +187,7 @@ pub(crate) fn current_os_id() -> u64 { /// 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: F) -> R +fn try_with_current(f: F) -> R where F: FnOnce(Option<&Thread>) -> R, { @@ -202,6 +205,36 @@ where } } +/// 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: F) -> R +where + F: FnOnce(Option<&str>) -> R, +{ + try_with_current(|thread| { + let name = if let Some(thread) = thread { + // If there is a current thread handle, try to use the name stored + // there. + thread.name() + } else if let Some(main) = main_thread::get() + && let Some(id) = 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. + Some("main") + } else { + None + }; + + f(name) + }) +} + /// Gets a handle to the thread that invokes it. If the handle stored in thread- /// local storage was already destroyed, this creates a new unnamed temporary /// handle to allow thread parking in nearly all situations. @@ -269,22 +302,13 @@ fn init_current(current: *mut ()) -> Thread { // BUSY exists solely for this check, but as it is in the slow path, the // extra TLS write above shouldn't matter. The alternative is nearly always // a stack overflow. - - // If you came across this message, contact the author of your allocator. - // If you are said author: A surprising amount of functions inside the - // standard library (e.g. `Mutex`, `thread_local!`, `File` when using long - // paths, even `panic!` when using unwinding), need memory allocation, so - // you'll get circular dependencies all over the place when using them. - // I (joboet) highly recommend using only APIs from core in your allocator - // and implementing your own system abstractions. Still, if you feel that - // a particular API should be entirely allocation-free, feel free to open - // an issue on the Rust repository, we'll see what we can do. + // + // If we reach this point it means our initialization routine ended up + // calling current() either directly, or indirectly through the global + // allocator, which is a bug either way as we may not call the global + // allocator in current(). rtabort!( - "\n\ - Attempted to access thread-local data while allocating said data.\n\ - Do not access functions that allocate in the global allocator!\n\ - This is a bug in the global allocator.\n\ - " + "init_current() was re-entrant, which indicates a bug in the Rust threading implementation" ) } else { debug_assert_eq!(current, DESTROYED); diff --git a/std/src/thread/functions.rs b/std/src/thread/functions.rs new file mode 100644 index 0000000000000..a25bae1aae31e --- /dev/null +++ b/std/src/thread/functions.rs @@ -0,0 +1,692 @@ +//! Free functions. + +use super::builder::Builder; +use super::current::current; +use super::join_handle::JoinHandle; +use crate::mem::forget; +use crate::num::NonZero; +use crate::sys::thread as imp; +use crate::time::{Duration, Instant}; +use crate::{io, panicking}; + +/// Spawns a new thread, returning a [`JoinHandle`] for it. +/// +/// The join handle provides a [`join`] method that can be used to join the spawned +/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing +/// the argument given to [`panic!`]. +/// +/// If the join handle is dropped, the spawned thread will implicitly be *detached*. +/// In this case, the spawned thread may no longer be joined. +/// (It is the responsibility of the program to either eventually join threads it +/// creates or detach them; otherwise, a resource leak will result.) +/// +/// This function creates a thread with the default parameters of [`Builder`]. +/// To specify the new thread's stack size or the name, use [`Builder::spawn`]. +/// +/// As you can see in the signature of `spawn` there are two constraints on +/// both the closure given to `spawn` and its return value, let's explain them: +/// +/// - The `'static` constraint means that the closure and its return value +/// must have a lifetime of the whole program execution. The reason for this +/// is that threads can outlive the lifetime they have been created in. +/// +/// Indeed if the thread, and by extension its return value, can outlive their +/// caller, we need to make sure that they will be valid afterwards, and since +/// we *can't* know when it will return we need to have them valid as long as +/// possible, that is until the end of the program, hence the `'static` +/// lifetime. +/// - The [`Send`] constraint is because the closure will need to be passed +/// *by value* from the thread where it is spawned to the new thread. Its +/// return value will need to be passed from the new thread to the thread +/// where it is `join`ed. +/// As a reminder, the [`Send`] marker trait expresses that it is safe to be +/// passed from thread to thread. [`Sync`] expresses that it is safe to have a +/// reference be passed from thread to thread. +/// +/// # Panics +/// +/// Panics if the OS fails to create a thread; use [`Builder::spawn`] +/// to recover from such errors. +/// +/// # Examples +/// +/// Creating a thread. +/// +/// ``` +/// use std::thread; +/// +/// let handler = thread::spawn(|| { +/// // thread code +/// }); +/// +/// handler.join().unwrap(); +/// ``` +/// +/// As mentioned in the module documentation, threads are usually made to +/// communicate using [`channels`], here is how it usually looks. +/// +/// This example also shows how to use `move`, in order to give ownership +/// of values to a thread. +/// +/// ``` +/// use std::thread; +/// use std::sync::mpsc::channel; +/// +/// let (tx, rx) = channel(); +/// +/// let sender = thread::spawn(move || { +/// tx.send("Hello, thread".to_owned()) +/// .expect("Unable to send on channel"); +/// }); +/// +/// let receiver = thread::spawn(move || { +/// let value = rx.recv().expect("Unable to receive from channel"); +/// println!("{value}"); +/// }); +/// +/// sender.join().expect("The sender thread has panicked"); +/// receiver.join().expect("The receiver thread has panicked"); +/// ``` +/// +/// A thread can also return a value through its [`JoinHandle`], you can use +/// this to make asynchronous computations (futures might be more appropriate +/// though). +/// +/// ``` +/// use std::thread; +/// +/// let computation = thread::spawn(|| { +/// // Some expensive computation. +/// 42 +/// }); +/// +/// let result = computation.join().unwrap(); +/// println!("{result}"); +/// ``` +/// +/// # Notes +/// +/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. +/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a +/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` +/// unwinds all the way to the root with such an exception, one of two behaviors are possible, +/// and it is unspecified which will occur: +/// +/// * The process aborts. +/// * The process does not abort, and [`join`] will return a `Result::Err` +/// containing an opaque type. +/// +/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html +/// [`channels`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +/// [`Err`]: crate::result::Result::Err +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(f).expect("failed to spawn thread") +} + +/// Cooperatively gives up a timeslice to the OS scheduler. +/// +/// This calls the underlying OS scheduler's yield primitive, signaling +/// that the calling thread is willing to give up its remaining timeslice +/// so that the OS may schedule other threads on the CPU. +/// +/// A drawback of yielding in a loop is that if the OS does not have any +/// other ready threads to run on the current CPU, the thread will effectively +/// busy-wait, which wastes CPU time and energy. +/// +/// Therefore, when waiting for events of interest, a programmer's first +/// choice should be to use synchronization devices such as [`channel`]s, +/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are +/// implemented in a blocking manner, giving up the CPU until the event +/// of interest has occurred which avoids repeated yielding. +/// +/// `yield_now` should thus be used only rarely, mostly in situations where +/// repeated polling is required because there is no other suitable way to +/// learn when an event of interest has occurred. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// thread::yield_now(); +/// ``` +/// +/// [`channel`]: crate::sync::mpsc +/// [`join`]: JoinHandle::join +/// [`Condvar`]: crate::sync::Condvar +/// [`Mutex`]: crate::sync::Mutex +#[stable(feature = "rust1", since = "1.0.0")] +pub fn yield_now() { + imp::yield_now() +} + +/// Determines whether the current thread is unwinding because of panic. +/// +/// A common use of this feature is to poison shared resources when writing +/// unsafe code, by checking `panicking` when the `drop` is called. +/// +/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] +/// already poison themselves when a thread panics while holding the lock. +/// +/// This can also be used in multithreaded applications, in order to send a +/// message to other threads warning that a thread has panicked (e.g., for +/// monitoring purposes). +/// +/// # Examples +/// +/// ```should_panic +/// use std::thread; +/// +/// struct SomeStruct; +/// +/// impl Drop for SomeStruct { +/// fn drop(&mut self) { +/// if thread::panicking() { +/// println!("dropped while unwinding"); +/// } else { +/// println!("dropped while not unwinding"); +/// } +/// } +/// } +/// +/// { +/// print!("a: "); +/// let a = SomeStruct; +/// } +/// +/// { +/// print!("b: "); +/// let b = SomeStruct; +/// panic!() +/// } +/// ``` +/// +/// [Mutex]: crate::sync::Mutex +#[inline] +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn panicking() -> bool { + panicking::panicking() +} + +/// Uses [`sleep`]. +/// +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// +/// # Examples +/// +/// ```no_run +/// use std::thread; +/// +/// // Let's sleep for 2 seconds: +/// thread::sleep_ms(2000); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] +pub fn sleep_ms(ms: u32) { + sleep(Duration::from_millis(ms as u64)) +} + +/// Puts the current thread to sleep for at least the specified amount of time. +/// +/// The thread may sleep longer than the duration specified due to scheduling +/// specifics or platform-dependent functionality. It will never sleep less. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// On Unix platforms, the underlying syscall may be interrupted by a +/// spurious wakeup or signal handler. To ensure the sleep occurs for at least +/// the specified duration, this function may invoke that system call multiple +/// times. +/// Platforms which do not support nanosecond precision for sleeping will +/// have `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// Currently, specifying a zero duration on Unix platforms returns immediately +/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows +/// platforms the underlying [`Sleep`] syscall is always invoked. +/// If the intention is to yield the current time-slice you may want to use +/// [`yield_now`] instead. +/// +/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep +/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep +/// +/// # Examples +/// +/// ```no_run +/// use std::{thread, time}; +/// +/// let ten_millis = time::Duration::from_millis(10); +/// let now = time::Instant::now(); +/// +/// thread::sleep(ten_millis); +/// +/// assert!(now.elapsed() >= ten_millis); +/// ``` +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub fn sleep(dur: Duration) { + imp::sleep(dur) +} + +/// Puts the current thread to sleep until the specified deadline has passed. +/// +/// The thread may still be asleep after the deadline specified due to +/// scheduling specifics or platform-dependent functionality. It will never +/// wake before. +/// +/// This function is blocking, and should not be used in `async` functions. +/// +/// # Platform-specific behavior +/// +/// In most cases this function will call an OS specific function. Where that +/// is not supported [`sleep`] is used. Those platforms are referred to as other +/// in the table below. +/// +/// # Underlying System calls +/// +/// The following system calls are [currently] being used: +/// +/// | Platform | System call | +/// |-----------|----------------------------------------------------------------------| +/// | Linux | [clock_nanosleep] (Monotonic clock) | +/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | +/// | Android | [clock_nanosleep] (Monotonic Clock)] | +/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | +/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | +/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | +/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | +/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] | +/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | +/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | +/// +/// [currently]: crate::io#platform-specific-behavior +/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep +/// +/// **Disclaimer:** These system calls might change over time. +/// +/// # Examples +/// +/// A simple game loop that limits the game to 60 frames per second. +/// +/// ```no_run +/// #![feature(thread_sleep_until)] +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// # fn update() {} +/// # fn render() {} +/// # +/// let max_fps = 60.0; +/// let frame_time = Duration::from_secs_f32(1.0/max_fps); +/// let mut next_frame = Instant::now(); +/// loop { +/// thread::sleep_until(next_frame); +/// next_frame += frame_time; +/// update(); +/// render(); +/// } +/// ``` +/// +/// A slow API we must not call too fast and which takes a few +/// tries before succeeding. By using `sleep_until` the time the +/// API call takes does not influence when we retry or when we give up +/// +/// ```no_run +/// #![feature(thread_sleep_until)] +/// # use std::time::{Duration, Instant}; +/// # use std::thread; +/// # +/// # enum Status { +/// # Ready(usize), +/// # Waiting, +/// # } +/// # fn slow_web_api_call() -> Status { Status::Ready(42) } +/// # +/// # const MAX_DURATION: Duration = Duration::from_secs(10); +/// # +/// # fn try_api_call() -> Result { +/// let deadline = Instant::now() + MAX_DURATION; +/// let delay = Duration::from_millis(250); +/// let mut next_attempt = Instant::now(); +/// loop { +/// if Instant::now() > deadline { +/// break Err(()); +/// } +/// if let Status::Ready(data) = slow_web_api_call() { +/// break Ok(data); +/// } +/// +/// next_attempt = deadline.min(next_attempt + delay); +/// thread::sleep_until(next_attempt); +/// } +/// # } +/// # let _data = try_api_call(); +/// ``` +#[unstable(feature = "thread_sleep_until", issue = "113752")] +pub fn sleep_until(deadline: Instant) { + imp::sleep_until(deadline) +} + +/// Used to ensure that `park` and `park_timeout` do not unwind, as that can +/// cause undefined behavior if not handled correctly (see #102398 for context). +struct PanicGuard; + +impl Drop for PanicGuard { + fn drop(&mut self) { + rtabort!("an irrecoverable error occurred while synchronizing threads") + } +} + +/// Blocks unless or until the current thread's token is made available. +/// +/// A call to `park` does not guarantee that the thread will remain parked +/// forever, and callers should be prepared for this possibility. However, +/// it is guaranteed that this function will not panic (it may abort the +/// process if the implementation encounters some rare errors). +/// +/// # `park` and `unpark` +/// +/// Every thread is equipped with some basic low-level blocking support, via the +/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] +/// method. [`park`] blocks the current thread, which can then be resumed from +/// another thread by calling the [`unpark`] method on the blocked thread's +/// handle. +/// +/// Conceptually, each [`Thread`] handle has an associated token, which is +/// initially not present: +/// +/// * The [`thread::park`][`park`] function blocks the current thread unless or +/// until the token is available for its thread handle, at which point it +/// atomically consumes the token. It may also return *spuriously*, without +/// consuming the token. [`thread::park_timeout`] does the same, but allows +/// specifying a maximum time to block the thread for. +/// +/// * The [`unpark`] method on a [`Thread`] atomically makes the token available +/// if it wasn't already. Because the token can be held by a thread even if it is currently not +/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. +/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens +/// after all `park` that may be done by other data structures! +/// +/// The API is typically used by acquiring a handle to the current thread, placing that handle in a +/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some +/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point +/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it +/// will be woken up properly. +/// +/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread +/// without first establishing that it is about to be `park`ing within your code, that `unpark` may +/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means +/// you must not call unknown code between setting up for parking and calling `park`; for instance, +/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a +/// deadlock. +/// +/// The motivation for this design is twofold: +/// +/// * It avoids the need to allocate mutexes and condvars when building new +/// synchronization primitives; the threads already provide basic +/// blocking/signaling. +/// +/// * It can be implemented very efficiently on many platforms. +/// +/// # Memory Ordering +/// +/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory +/// operations performed before a call to `unpark` are made visible to the thread that +/// consumes the token and returns from `park`. Note that all `park` and `unpark` +/// operations for a given thread form a total order and _all_ prior `unpark` operations +/// synchronize-with `park`. +/// +/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` +/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same +/// thread form a [release sequence]. +/// +/// Note that being unblocked does not imply a call was made to `unpark`, because +/// wakeups can also be spurious. For example, a valid, but inefficient, +/// implementation could have `park` and `unpark` return immediately without doing anything, +/// making *all* wakeups spurious. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// use std::sync::atomic::{Ordering, AtomicBool}; +/// use std::time::Duration; +/// +/// static QUEUED: AtomicBool = AtomicBool::new(false); +/// static FLAG: AtomicBool = AtomicBool::new(false); +/// +/// let parked_thread = thread::spawn(move || { +/// println!("Thread spawned"); +/// // Signal that we are going to `park`. Between this store and our `park`, there may +/// // be no other `park`, or else that `park` could consume our `unpark` token! +/// QUEUED.store(true, Ordering::Release); +/// // We want to wait until the flag is set. We *could* just spin, but using +/// // park/unpark is more efficient. +/// while !FLAG.load(Ordering::Acquire) { +/// // We can *not* use `println!` here since that could use thread parking internally. +/// thread::park(); +/// // We *could* get here spuriously, i.e., way before the 10ms below are over! +/// // But that is no problem, we are in a loop until the flag is set anyway. +/// } +/// println!("Flag received"); +/// }); +/// +/// // Let some time pass for the thread to be spawned. +/// thread::sleep(Duration::from_millis(10)); +/// +/// // Ensure the thread is about to park. +/// // This is crucial! It guarantees that the `unpark` below is not consumed +/// // by some other code in the parked thread (e.g. inside `println!`). +/// while !QUEUED.load(Ordering::Acquire) { +/// // Spinning is of course inefficient; in practice, this would more likely be +/// // a dequeue where we have no work to do if there's nobody queued. +/// std::hint::spin_loop(); +/// } +/// +/// // Set the flag, and let the thread wake up. +/// // There is no race condition here: if `unpark` +/// // happens first, `park` will return immediately. +/// // There is also no other `park` that could consume this token, +/// // since we waited until the other thread got queued. +/// // Hence there is no risk of a deadlock. +/// FLAG.store(true, Ordering::Release); +/// println!("Unpark the thread"); +/// parked_thread.thread().unpark(); +/// +/// parked_thread.join().unwrap(); +/// ``` +/// +/// [`Thread`]: super::Thread +/// [`unpark`]: super::Thread::unpark +/// [`thread::park_timeout`]: park_timeout +/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence +#[stable(feature = "rust1", since = "1.0.0")] +pub fn park() { + let guard = PanicGuard; + // SAFETY: park_timeout is called on the parker owned by this thread. + unsafe { + current().park(); + } + // No panic occurred, do not abort. + forget(guard); +} + +/// Uses [`park_timeout`]. +/// +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`] except +/// that the thread will be blocked for roughly no longer than `dur`. This +/// method should not be used for precise timing due to anomalies such as +/// preemption or platform differences that might not cause the maximum +/// amount of time waited to be precisely `ms` long. +/// +/// See the [park documentation][`park`] for more detail. +#[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] +pub fn park_timeout_ms(ms: u32) { + park_timeout(Duration::from_millis(ms as u64)) +} + +/// Blocks unless or until the current thread's token is made available or +/// the specified duration has been reached (may wake spuriously). +/// +/// The semantics of this function are equivalent to [`park`][park] except +/// that the thread will be blocked for roughly no longer than `dur`. This +/// method should not be used for precise timing due to anomalies such as +/// preemption or platform differences that might not cause the maximum +/// amount of time waited to be precisely `dur` long. +/// +/// See the [park documentation][park] for more details. +/// +/// # Platform-specific behavior +/// +/// Platforms which do not support nanosecond precision for sleeping will have +/// `dur` rounded up to the nearest granularity of time they can sleep for. +/// +/// # Examples +/// +/// Waiting for the complete expiration of the timeout: +/// +/// ```rust,no_run +/// use std::thread::park_timeout; +/// use std::time::{Instant, Duration}; +/// +/// let timeout = Duration::from_secs(2); +/// let beginning_park = Instant::now(); +/// +/// let mut timeout_remaining = timeout; +/// loop { +/// park_timeout(timeout_remaining); +/// let elapsed = beginning_park.elapsed(); +/// if elapsed >= timeout { +/// break; +/// } +/// println!("restarting park_timeout after {elapsed:?}"); +/// timeout_remaining = timeout - elapsed; +/// } +/// ``` +#[stable(feature = "park_timeout", since = "1.4.0")] +pub fn park_timeout(dur: Duration) { + let guard = PanicGuard; + // SAFETY: park_timeout is called on a handle owned by this thread. + unsafe { + current().park_timeout(dur); + } + // No panic occurred, do not abort. + forget(guard); +} + +/// Returns an estimate of the default amount of parallelism a program should use. +/// +/// Parallelism is a resource. A given machine provides a certain capacity for +/// parallelism, i.e., a bound on the number of computations it can perform +/// simultaneously. This number often corresponds to the amount of CPUs a +/// computer has, but it may diverge in various cases. +/// +/// Host environments such as VMs or container orchestrators may want to +/// restrict the amount of parallelism made available to programs in them. This +/// is often done to limit the potential impact of (unintentionally) +/// resource-intensive programs on other programs running on the same machine. +/// +/// # Limitations +/// +/// The purpose of this API is to provide an easy and portable way to query +/// the default amount of parallelism the program should use. Among other things it +/// does not expose information on NUMA regions, does not account for +/// differences in (co)processor capabilities or current system load, +/// and will not modify the program's global state in order to more accurately +/// query the amount of available parallelism. +/// +/// Where both fixed steady-state and burst limits are available the steady-state +/// capacity will be used to ensure more predictable latencies. +/// +/// Resource limits can be changed during the runtime of a program, therefore the value is +/// not cached and instead recomputed every time this function is called. It should not be +/// called from hot code. +/// +/// The value returned by this function should be considered a simplified +/// approximation of the actual amount of parallelism available at any given +/// time. To get a more detailed or precise overview of the amount of +/// parallelism available to the program, you may wish to use +/// platform-specific APIs as well. The following platform limitations currently +/// apply to `available_parallelism`: +/// +/// On Windows: +/// - It may undercount the amount of parallelism available on systems with more +/// than 64 logical CPUs. However, programs typically need specific support to +/// take advantage of more than 64 logical CPUs, and in the absence of such +/// support, the number returned by this function accurately reflects the +/// number of logical CPUs the program can use by default. +/// - It may overcount the amount of parallelism available on systems limited by +/// process-wide affinity masks, or job object limitations. +/// +/// On Linux: +/// - It may overcount the amount of parallelism available when limited by a +/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be +/// queried, e.g. due to sandboxing. +/// - It may undercount the amount of parallelism if the current thread's affinity mask +/// does not reflect the process' cpuset, e.g. due to pinned threads. +/// - If the process is in a cgroup v1 cpu controller, this may need to +/// scan mountpoints to find the corresponding cgroup v1 controller, +/// which may take time on systems with large numbers of mountpoints. +/// (This does not apply to cgroup v2, or to processes not in a +/// cgroup.) +/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of +/// threads, `available_parallelism` cannot know how much of that limit a Rust program should +/// take, or know in a reliable and race-free way how much of that limit is already taken. +/// +/// On all targets: +/// - It may overcount the amount of parallelism available when running in a VM +/// with CPU usage limits (e.g. an overcommitted host). +/// +/// # Errors +/// +/// This function will, but is not limited to, return errors in the following +/// cases: +/// +/// - If the amount of parallelism is not known for the target platform. +/// - If the program lacks permission to query the amount of parallelism made +/// available to it. +/// +/// # Examples +/// +/// ``` +/// # #![allow(dead_code)] +/// use std::{io, thread}; +/// +/// fn main() -> io::Result<()> { +/// let count = thread::available_parallelism()?.get(); +/// assert!(count >= 1_usize); +/// Ok(()) +/// } +/// ``` +#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. +#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. +#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. +#[stable(feature = "available_parallelism", since = "1.59.0")] +pub fn available_parallelism() -> io::Result> { + imp::available_parallelism() +} diff --git a/std/src/thread/id.rs b/std/src/thread/id.rs new file mode 100644 index 0000000000000..3da0825db604d --- /dev/null +++ b/std/src/thread/id.rs @@ -0,0 +1,120 @@ +use crate::num::NonZero; +use crate::sync::atomic::{Atomic, Ordering}; + +/// A unique identifier for a running thread. +/// +/// A `ThreadId` is an opaque object that uniquely identifies each thread +/// created during the lifetime of a process. `ThreadId`s are guaranteed not to +/// be reused, even when a thread terminates. `ThreadId`s are under the control +/// of Rust's standard library and there may not be any relationship between +/// `ThreadId` and the underlying platform's notion of a thread identifier -- +/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId` +/// can be retrieved from the [`id`] method on a [`Thread`]. +/// +/// # Examples +/// +/// ``` +/// use std::thread; +/// +/// let other_thread = thread::spawn(|| { +/// thread::current().id() +/// }); +/// +/// let other_thread_id = other_thread.join().unwrap(); +/// assert!(thread::current().id() != other_thread_id); +/// ``` +/// +/// [`Thread`]: super::Thread +/// [`id`]: super::Thread::id +#[stable(feature = "thread_id", since = "1.19.0")] +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +pub struct ThreadId(NonZero); + +impl ThreadId { + // Generate a new unique thread ID. + pub(crate) fn new() -> ThreadId { + #[cold] + fn exhausted() -> ! { + panic!("failed to generate unique thread ID: bitspace exhausted") + } + + cfg_select! { + target_has_atomic = "64" => { + use crate::sync::atomic::AtomicU64; + + static COUNTER: Atomic = AtomicU64::new(0); + + let mut last = COUNTER.load(Ordering::Relaxed); + loop { + let Some(id) = last.checked_add(1) else { + exhausted(); + }; + + match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => return ThreadId(NonZero::new(id).unwrap()), + Err(id) => last = id, + } + } + } + _ => { + use crate::cell::SyncUnsafeCell; + use crate::hint::spin_loop; + use crate::sync::atomic::AtomicBool; + use crate::thread::yield_now; + + // If we don't have a 64-bit atomic we use a small spinlock. We don't use Mutex + // here as we might be trying to get the current thread id in the global allocator, + // and on some platforms Mutex requires allocation. + static COUNTER_LOCKED: Atomic = AtomicBool::new(false); + static COUNTER: SyncUnsafeCell = SyncUnsafeCell::new(0); + + // Acquire lock. + let mut spin = 0; + // Miri doesn't like it when we yield here as it interferes with deterministically + // scheduling threads, so avoid `compare_exchange_weak` to avoid spurious yields. + while COUNTER_LOCKED.swap(true, Ordering::Acquire) { + if spin <= 3 { + for _ in 0..(1 << spin) { + spin_loop(); + } + } else { + yield_now(); + } + spin += 1; + } + // This was `false` before the swap, so we got the lock. + + // SAFETY: we have an exclusive lock on the counter. + unsafe { + if let Some(id) = (*COUNTER.get()).checked_add(1) { + *COUNTER.get() = id; + COUNTER_LOCKED.store(false, Ordering::Release); + ThreadId(NonZero::new(id).unwrap()) + } else { + COUNTER_LOCKED.store(false, Ordering::Release); + exhausted() + } + } + } + } + } + + #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] + pub(super) fn from_u64(v: u64) -> Option { + NonZero::new(v).map(ThreadId) + } + + /// This returns a numeric identifier for the thread identified by this + /// `ThreadId`. + /// + /// As noted in the documentation for the type itself, it is essentially an + /// opaque ID, but is guaranteed to be unique for each thread. The returned + /// value is entirely opaque -- only equality testing is stable. Note that + /// it is not guaranteed which values new threads will return, and this may + /// change across Rust versions. + #[must_use] + #[unstable(feature = "thread_id_value", issue = "67939")] + pub fn as_u64(&self) -> NonZero { + self.0 + } +} diff --git a/std/src/thread/join_handle.rs b/std/src/thread/join_handle.rs new file mode 100644 index 0000000000000..8714ceeb4f467 --- /dev/null +++ b/std/src/thread/join_handle.rs @@ -0,0 +1,185 @@ +use super::Result; +use super::lifecycle::JoinInner; +use super::thread::Thread; +use crate::fmt; +use crate::sys::thread as imp; +use crate::sys_common::{AsInner, IntoInner}; + +/// An owned permission to join on a thread (block on its termination). +/// +/// A `JoinHandle` *detaches* the associated thread when it is dropped, which +/// means that there is no longer any handle to the thread and no way to `join` +/// on it. +/// +/// Due to platform restrictions, it is not possible to [`Clone`] this +/// handle: the ability to join a thread is a uniquely-owned permission. +/// +/// This `struct` is created by the [`thread::spawn`] function and the +/// [`thread::Builder::spawn`] method. +/// +/// # Examples +/// +/// Creation from [`thread::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { +/// // some work here +/// }); +/// ``` +/// +/// Creation from [`thread::Builder::spawn`]: +/// +/// ``` +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { +/// // some work here +/// }).unwrap(); +/// ``` +/// +/// A thread being detached and outliving the thread that spawned it: +/// +/// ```no_run +/// use std::thread; +/// use std::time::Duration; +/// +/// let original_thread = thread::spawn(|| { +/// let _detached_thread = thread::spawn(|| { +/// // Here we sleep to make sure that the first thread returns before. +/// thread::sleep(Duration::from_millis(10)); +/// // This will be called, even though the JoinHandle is dropped. +/// println!("♫ Still alive ♫"); +/// }); +/// }); +/// +/// original_thread.join().expect("The thread being joined has panicked"); +/// println!("Original thread is joined."); +/// +/// // We make sure that the new thread has time to run, before the main +/// // thread returns. +/// +/// thread::sleep(Duration::from_millis(1000)); +/// ``` +/// +/// [`thread::Builder::spawn`]: super::Builder::spawn +/// [`thread::spawn`]: super::spawn +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(target_os = "teeos", must_use)] +pub struct JoinHandle(pub(super) JoinInner<'static, T>); + +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Send for JoinHandle {} +#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] +unsafe impl Sync for JoinHandle {} + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// + /// let thread = join_handle.thread(); + /// println!("thread id: {:?}", thread.id()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn thread(&self) -> &Thread { + self.0.thread() + } + + /// Waits for the associated thread to finish. + /// + /// This function will return immediately if the associated thread has already finished. + /// + /// In terms of [atomic memory orderings], the completion of the associated + /// thread synchronizes with this function returning. In other words, all + /// operations performed by that thread [happen + /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all + /// operations that happen after `join` returns. + /// + /// If the associated thread panics, [`Err`] is returned with the parameter given + /// to [`panic!`] (though see the Notes below). + /// + /// [`Err`]: crate::result::Result::Err + /// [atomic memory orderings]: crate::sync::atomic + /// + /// # Panics + /// + /// This function may panic on some platforms if a thread attempts to join + /// itself or otherwise may create a deadlock with joining threads. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { + /// // some work here + /// }).unwrap(); + /// join_handle.join().expect("Couldn't join on the associated thread"); + /// ``` + /// + /// # Notes + /// + /// If a "foreign" unwinding operation (e.g. an exception thrown from C++ + /// code, or a `panic!` in Rust code compiled or linked with a different + /// runtime) unwinds all the way to the thread root, the process may be + /// aborted; see the Notes on [`thread::spawn`]. If the process is not + /// aborted, this function will return a `Result::Err` containing an opaque + /// type. + /// + /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html + /// [`thread::spawn`]: super::spawn + #[stable(feature = "rust1", since = "1.0.0")] + pub fn join(self) -> Result { + self.0.join() + } + + /// Checks if the associated thread has finished running its main function. + /// + /// `is_finished` supports implementing a non-blocking join operation, by checking + /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To + /// block while waiting on the thread to finish, use [`join`][Self::join]. + /// + /// This might return `true` for a brief moment after the thread's main + /// function has returned, but before the thread itself has stopped running. + /// However, once this returns `true`, [`join`][Self::join] can be expected + /// to return quickly, without blocking for any significant amount of time. + #[stable(feature = "thread_is_running", since = "1.61.0")] + pub fn is_finished(&self) -> bool { + self.0.is_finished() + } +} + +impl AsInner for JoinHandle { + fn as_inner(&self) -> &imp::Thread { + self.0.as_inner() + } +} + +impl IntoInner for JoinHandle { + fn into_inner(self) -> imp::Thread { + self.0.into_inner() + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JoinHandle").finish_non_exhaustive() + } +} diff --git a/std/src/thread/lifecycle.rs b/std/src/thread/lifecycle.rs new file mode 100644 index 0000000000000..119322b909b52 --- /dev/null +++ b/std/src/thread/lifecycle.rs @@ -0,0 +1,261 @@ +//! The inner logic for thread spawning and joining. + +use super::current::set_current; +use super::id::ThreadId; +use super::scoped::ScopeData; +use super::thread::Thread; +use super::{Result, spawnhook}; +use crate::cell::UnsafeCell; +use crate::marker::PhantomData; +use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::sync::Arc; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sys::thread as imp; +use crate::sys_common::{AsInner, IntoInner}; +use crate::{env, io, panic}; + +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +pub(super) unsafe fn spawn_unchecked<'scope, F, T>( + name: Option, + stack_size: Option, + no_hooks: bool, + scope_data: Option>, + f: F, +) -> io::Result> +where + F: FnOnce() -> T, + F: Send, + T: Send, +{ + let stack_size = stack_size.unwrap_or_else(|| { + static MIN: Atomic = AtomicUsize::new(0); + + match MIN.load(Ordering::Relaxed) { + 0 => {} + n => return n - 1, + } + + let amt = env::var_os("RUST_MIN_STACK") + .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) + .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); + + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + MIN.store(amt + 1, Ordering::Relaxed); + amt + }); + + let id = ThreadId::new(); + let thread = Thread::new(id, name); + + let hooks = if no_hooks { + spawnhook::ChildSpawnHooks::default() + } else { + spawnhook::run_spawn_hooks(&thread) + }; + + let my_packet: Arc> = + Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData }); + let their_packet = my_packet.clone(); + + // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. + // See for more details. + // To prevent leaks we use a wrapper that drops its contents. + #[repr(transparent)] + struct MaybeDangling(MaybeUninit); + impl MaybeDangling { + fn new(x: T) -> Self { + MaybeDangling(MaybeUninit::new(x)) + } + fn into_inner(self) -> T { + // Make sure we don't drop. + let this = ManuallyDrop::new(self); + // SAFETY: we are always initialized. + unsafe { this.0.assume_init_read() } + } + } + impl Drop for MaybeDangling { + fn drop(&mut self) { + // SAFETY: we are always initialized. + unsafe { self.0.assume_init_drop() }; + } + } + + let f = MaybeDangling::new(f); + + // The entrypoint of the Rust thread, after platform-specific thread + // initialization is done. + let rust_start = move || { + let f = f.into_inner(); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { + crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); + crate::sys::backtrace::__rust_begin_short_backtrace(f) + })); + // SAFETY: `their_packet` as been built just above and moved by the + // closure (it is an Arc<...>) and `my_packet` will be stored in the + // same `JoinInner` as this closure meaning the mutation will be + // safe (not modify it and affect a value far away). + unsafe { *their_packet.result.get() = Some(try_result) }; + // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that + // will call `decrement_num_running_threads` and therefore signal that this thread is + // done. + drop(their_packet); + // Here, the lifetime `'scope` can end. `main` keeps running for a bit + // after that before returning itself. + }; + + if let Some(scope_data) = &my_packet.scope { + scope_data.increment_num_running_threads(); + } + + // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the + // lifetime change is justified. + let rust_start = unsafe { + Box::from_raw(Box::into_raw(Box::new(rust_start)) as *mut (dyn FnOnce() + Send + 'static)) + }; + + let init = Box::new(ThreadInit { handle: thread.clone(), rust_start }); + + Ok(JoinInner { + // SAFETY: + // + // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed + // through FFI or otherwise used with low-level threading primitives that have no + // notion of or way to enforce lifetimes. + // + // As mentioned in the `Safety` section of this function's documentation, the caller of + // this function needs to guarantee that the passed-in lifetime is sufficiently long + // for the lifetime of the thread. + // + // Similarly, the `sys` implementation must guarantee that no references to the closure + // exist after the thread has terminated, which is signaled by `Thread::join` + // returning. + native: unsafe { imp::Thread::new(stack_size, init)? }, + thread, + packet: my_packet, + }) +} + +/// The data passed to the spawned thread for thread initialization. Any thread +/// implementation should start a new thread by calling .init() on this before +/// doing anything else to ensure the current thread is properly initialized and +/// the global allocator works. +pub(crate) struct ThreadInit { + pub handle: Thread, + pub rust_start: Box, +} + +impl ThreadInit { + /// Initialize the 'current thread' mechanism on this thread, returning the + /// Rust entry point. + pub fn init(self: Box) -> Box { + // Set the current thread before any (de)allocations on the global allocator occur, + // so that it may call std::thread::current() in its implementation. This is also + // why we take Box, to ensure the Box is not destroyed until after this point. + // Cloning the handle does not invoke the global allocator, it is an Arc. + if let Err(_thread) = set_current(self.handle.clone()) { + // The current thread should not have set yet. Use an abort to save binary size (see #123356). + rtabort!("current thread handle already set during thread spawn"); + } + + if let Some(name) = self.handle.cname() { + imp::set_name(name); + } + + self.rust_start + } +} + +// This packet is used to communicate the return value between the spawned +// thread and the rest of the program. It is shared through an `Arc` and +// there's no need for a mutex here because synchronization happens with `join()` +// (the caller will never read this packet until the thread has exited). +// +// An Arc to the packet is stored into a `JoinInner` which in turns is placed +// in `JoinHandle`. +struct Packet<'scope, T> { + scope: Option>, + result: UnsafeCell>>, + _marker: PhantomData>, +} + +// Due to the usage of `UnsafeCell` we need to manually implement Sync. +// The type `T` should already always be Send (otherwise the thread could not +// have been created) and the Packet is Sync because all access to the +// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. +unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} + +impl<'scope, T> Drop for Packet<'scope, T> { + fn drop(&mut self) { + // If this packet was for a thread that ran in a scope, the thread + // panicked, and nobody consumed the panic payload, we make sure + // the scope function will panic. + let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + // Drop the result without causing unwinding. + // This is only relevant for threads that aren't join()ed, as + // join() will take the `result` and set it to None, such that + // there is nothing left to drop here. + // If this panics, we should handle that, because we're outside the + // outermost `catch_unwind` of our thread. + // We just abort in that case, since there's nothing else we can do. + // (And even if we tried to handle it somehow, we'd also need to handle + // the case where the panic payload we get out of it also panics on + // drop, and so on. See issue #86027.) + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { + *self.result.get_mut() = None; + })) { + rtabort!("thread result panicked on drop"); + } + // Book-keeping so the scope knows when it's done. + if let Some(scope) = &self.scope { + // Now that there will be no more user code running on this thread + // that can use 'scope, mark the thread as 'finished'. + // It's important we only do this after the `result` has been dropped, + // since dropping it might still use things it borrowed from 'scope. + scope.decrement_num_running_threads(unhandled_panic); + } + } +} + +/// Inner representation for JoinHandle +pub(super) struct JoinInner<'scope, T> { + native: imp::Thread, + thread: Thread, + packet: Arc>, +} + +impl<'scope, T> JoinInner<'scope, T> { + pub(super) fn is_finished(&self) -> bool { + Arc::strong_count(&self.packet) == 1 + } + + pub(super) fn thread(&self) -> &Thread { + &self.thread + } + + pub(super) fn join(mut self) -> Result { + self.native.join(); + Arc::get_mut(&mut self.packet) + // FIXME(fuzzypixelz): returning an error instead of panicking here + // would require updating the documentation of + // `std::thread::Result`; currently we can return `Err` if and only + // if the thread had panicked. + .expect("threads should not terminate unexpectedly") + .result + .get_mut() + .take() + .unwrap() + } +} + +impl AsInner for JoinInner<'static, T> { + fn as_inner(&self) -> &imp::Thread { + &self.native + } +} + +impl IntoInner for JoinInner<'static, T> { + fn into_inner(self) -> imp::Thread { + self.native + } +} diff --git a/std/src/thread/local.rs b/std/src/thread/local.rs index 4259a4d1f3b7c..06e4b252fc8f3 100644 --- a/std/src/thread/local.rs +++ b/std/src/thread/local.rs @@ -24,12 +24,17 @@ use crate::fmt; /// [`with`]) within a thread, and values that implement [`Drop`] get /// destructed when a thread exits. Some platform-specific caveats apply, which /// are explained below. -/// Note that if the destructor panics, the whole process will be [aborted]. +/// Note that, should the destructor panic, the whole process will be [aborted]. +/// On platforms where initialization requires memory allocation, this is +/// performed directly through [`System`], allowing the [global allocator] +/// to make use of thread local storage. /// /// A `LocalKey`'s initializer cannot recursively depend on itself. Using a /// `LocalKey` in this way may cause panics, aborts, or infinite recursion on /// the first call to `with`. /// +/// [`System`]: crate::alloc::System +/// [global allocator]: crate::alloc /// [aborted]: crate::process::abort /// /// # Single-thread Synchronization diff --git a/std/src/thread/main_thread.rs b/std/src/thread/main_thread.rs new file mode 100644 index 0000000000000..394074a593674 --- /dev/null +++ b/std/src/thread/main_thread.rs @@ -0,0 +1,56 @@ +//! 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. + +cfg_select! { + target_has_atomic = "64" => { + use super::id::ThreadId; + use crate::sync::atomic::{Atomic, AtomicU64}; + use crate::sync::atomic::Ordering::Relaxed; + + static MAIN: Atomic = AtomicU64::new(0); + + pub(super) fn get() -> Option { + ThreadId::from_u64(MAIN.load(Relaxed)) + } + + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + MAIN.store(id.as_u64().get(), Relaxed) + } + } + _ => { + use super::id::ThreadId; + use crate::mem::MaybeUninit; + use crate::sync::atomic::{Atomic, AtomicBool}; + use crate::sync::atomic::Ordering::{Acquire, Release}; + + static INIT: Atomic = AtomicBool::new(false); + static mut MAIN: MaybeUninit = MaybeUninit::uninit(); + + pub(super) fn get() -> Option { + 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); + } + } +} diff --git a/std/src/thread/mod.rs b/std/src/thread/mod.rs index 16313da8e178c..00aeb70e6e076 100644 --- a/std/src/thread/mod.rs +++ b/std/src/thread/mod.rs @@ -137,6 +137,7 @@ //! Note that the stack size of the main thread is *not* determined by Rust. //! //! [channels]: crate::sync::mpsc +//! [`Arc`]: crate::sync::Arc //! [`join`]: JoinHandle::join //! [`Result`]: crate::result::Result //! [`Ok`]: crate::result::Result::Ok @@ -155,53 +156,56 @@ // Under `test`, `__FastLocalKeyInner` seems unused. #![cfg_attr(test, allow(dead_code))] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - 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; -use crate::pin::Pin; -use crate::sync::Arc; -use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sys::sync::Parker; -use crate::sys::thread as imp; -use crate::sys_common::{AsInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{env, fmt, io, panic, panicking, str}; -#[stable(feature = "scoped_threads", since = "1.63.0")] +#[macro_use] +mod local; +mod builder; +mod current; +mod functions; +mod id; +mod join_handle; +mod lifecycle; mod scoped; +mod spawnhook; +mod thread; -#[stable(feature = "scoped_threads", since = "1.63.0")] -pub use scoped::{Scope, ScopedJoinHandle, scope}; +pub(crate) mod main_thread; -mod current; +#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] +mod tests; +#[stable(feature = "rust1", since = "1.0.0")] +pub use builder::Builder; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; #[unstable(feature = "current_thread_id", issue = "147194")] pub use current::current_id; -pub(crate) use current::{current_or_unnamed, current_os_id, drop_current}; -use current::{set_current, try_with_current}; - -mod spawnhook; - +pub(crate) use current::{current_or_unnamed, current_os_id, drop_current, with_current_name}; +#[stable(feature = "available_parallelism", since = "1.59.0")] +pub use functions::available_parallelism; +#[stable(feature = "park_timeout", since = "1.4.0")] +pub use functions::park_timeout; +#[stable(feature = "thread_sleep", since = "1.4.0")] +pub use functions::sleep; +#[unstable(feature = "thread_sleep_until", issue = "113752")] +pub use functions::sleep_until; +#[expect(deprecated)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use functions::{panicking, park, park_timeout_ms, sleep_ms, spawn, yield_now}; +#[stable(feature = "thread_id", since = "1.19.0")] +pub use id::ThreadId; +#[stable(feature = "rust1", since = "1.0.0")] +pub use join_handle::JoinHandle; +pub(crate) use lifecycle::ThreadInit; +#[stable(feature = "rust1", since = "1.0.0")] +pub use local::{AccessError, LocalKey}; +#[stable(feature = "scoped_threads", since = "1.63.0")] +pub use scoped::{Scope, ScopedJoinHandle, scope}; #[unstable(feature = "thread_spawn_hook", issue = "132951")] pub use spawnhook::add_spawn_hook; - -//////////////////////////////////////////////////////////////////////////////// -// Thread-local storage -//////////////////////////////////////////////////////////////////////////////// - -#[macro_use] -mod local; - #[stable(feature = "rust1", since = "1.0.0")] -pub use self::local::{AccessError, LocalKey}; +pub use thread::Thread; // Implementation details used by the thread_local!{} macro. #[doc(hidden)] @@ -211,1494 +215,6 @@ pub mod local_impl { pub use crate::sys::thread_local::*; } -//////////////////////////////////////////////////////////////////////////////// -// Builder -//////////////////////////////////////////////////////////////////////////////// - -/// Thread factory, which can be used in order to configure the properties of -/// a new thread. -/// -/// Methods can be chained on it in order to configure it. -/// -/// The two configurations available are: -/// -/// - [`name`]: specifies an [associated name for the thread][naming-threads] -/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size] -/// -/// The [`spawn`] method will take ownership of the builder and create an -/// [`io::Result`] to the thread handle with the given configuration. -/// -/// The [`thread::spawn`] free function uses a `Builder` with default -/// configuration and [`unwrap`]s its return value. -/// -/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want -/// to recover from a failure to launch a thread, indeed the free function will -/// panic where the `Builder` method will return a [`io::Result`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let handler = builder.spawn(|| { -/// // thread code -/// }).unwrap(); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// [`stack_size`]: Builder::stack_size -/// [`name`]: Builder::name -/// [`spawn`]: Builder::spawn -/// [`thread::spawn`]: spawn -/// [`io::Result`]: crate::io::Result -/// [`unwrap`]: crate::result::Result::unwrap -/// [naming-threads]: ./index.html#naming-threads -/// [stack-size]: ./index.html#stack-size -#[must_use = "must eventually spawn the thread"] -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Builder { - // A name for the thread-to-be, for identification in panic messages - name: Option, - // The size of the stack for the spawned thread in bytes - stack_size: Option, - // Skip running and inheriting the thread spawn hooks - no_hooks: bool, -} - -impl Builder { - /// Generates the base configuration for spawning a thread, from which - /// configuration methods can be chained. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()) - /// .stack_size(32 * 1024); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Builder { - Builder { name: None, stack_size: None, no_hooks: false } - } - - /// Names the thread-to-be. Currently the name is used for identification - /// only in panic messages. - /// - /// The name must not contain null bytes (`\0`). - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Sets the size of the stack (in bytes) for the new thread. - /// - /// The actual stack size may be greater than this value if - /// the platform specifies a minimal stack size. - /// - /// For more information about the stack size for threads, see - /// [this module-level documentation][stack-size]. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new().stack_size(32 * 1024); - /// ``` - /// - /// [stack-size]: ./index.html#stack-size - #[stable(feature = "rust1", since = "1.0.0")] - pub fn stack_size(mut self, size: usize) -> Builder { - self.stack_size = Some(size); - self - } - - /// Disables running and inheriting [spawn hooks](add_spawn_hook). - /// - /// Use this if the parent thread is in no way relevant for the child thread. - /// For example, when lazily spawning threads for a thread pool. - #[unstable(feature = "thread_spawn_hook", issue = "132951")] - pub fn no_hooks(mut self) -> Builder { - self.no_hooks = true; - self - } - - /// Spawns a new thread by taking ownership of the `Builder`, and returns an - /// [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// [`io::Result`]: crate::io::Result - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// // thread code - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub fn spawn(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - unsafe { self.spawn_unchecked(f) } - } - - /// Spawns a new thread without any lifetime restrictions by taking ownership - /// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread, including recovering its panics. - /// - /// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`], - /// except for the relaxed lifetime bounds, which render it unsafe. - /// For a more complete documentation see [`thread::spawn`][`spawn`]. - /// - /// # Errors - /// - /// Unlike the [`spawn`] free function, this method yields an - /// [`io::Result`] to capture any failure to create the thread at - /// the OS level. - /// - /// # Panics - /// - /// Panics if a thread name was set and it contained null bytes. - /// - /// # Safety - /// - /// The caller has to ensure that the spawned thread does not outlive any - /// references in the supplied thread closure and its return type. - /// This can be guaranteed in two ways: - /// - /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced - /// data is dropped - /// - use only types with `'static` lifetime bounds, i.e., those with no or only - /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] - /// and [`thread::spawn`][`spawn`] enforce this property statically) - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let x = 1; - /// let thread_x = &x; - /// - /// let handler = unsafe { - /// builder.spawn_unchecked(move || { - /// println!("x = {}", *thread_x); - /// }).unwrap() - /// }; - /// - /// // caller has to ensure `join()` is called, otherwise - /// // it is possible to access freed memory if `x` gets - /// // dropped before the thread closure is executed! - /// handler.join().unwrap(); - /// ``` - /// - /// [`io::Result`]: crate::io::Result - #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send, - T: Send, - { - Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) - } - - #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - unsafe fn spawn_unchecked_<'scope, F, T>( - self, - f: F, - scope_data: Option>, - ) -> io::Result> - where - F: FnOnce() -> T, - F: Send, - T: Send, - { - let Builder { name, stack_size, no_hooks } = self; - - let stack_size = stack_size.unwrap_or_else(|| { - static MIN: Atomic = AtomicUsize::new(0); - - match MIN.load(Ordering::Relaxed) { - 0 => {} - n => return n - 1, - } - - let amt = env::var_os("RUST_MIN_STACK") - .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) - .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE); - - // 0 is our sentinel value, so ensure that we'll never see 0 after - // initialization has run - MIN.store(amt + 1, Ordering::Relaxed); - amt - }); - - let id = ThreadId::new(); - let my_thread = Thread::new(id, name); - - let hooks = if no_hooks { - spawnhook::ChildSpawnHooks::default() - } else { - spawnhook::run_spawn_hooks(&my_thread) - }; - - let their_thread = my_thread.clone(); - - let my_packet: Arc> = Arc::new(Packet { - scope: scope_data, - result: UnsafeCell::new(None), - _marker: PhantomData, - }); - let their_packet = my_packet.clone(); - - // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. - // See for more details. - // To prevent leaks we use a wrapper that drops its contents. - #[repr(transparent)] - struct MaybeDangling(mem::MaybeUninit); - impl MaybeDangling { - fn new(x: T) -> Self { - MaybeDangling(mem::MaybeUninit::new(x)) - } - fn into_inner(self) -> T { - // Make sure we don't drop. - let this = ManuallyDrop::new(self); - // SAFETY: we are always initialized. - unsafe { this.0.assume_init_read() } - } - } - impl Drop for MaybeDangling { - fn drop(&mut self) { - // SAFETY: we are always initialized. - unsafe { self.0.assume_init_drop() }; - } - } - - let f = MaybeDangling::new(f); - let main = move || { - if let Err(_thread) = set_current(their_thread.clone()) { - // Both the current thread handle and the ID should not be - // initialized yet. Since only the C runtime and some of our - // platform code run before this, this point shouldn't be - // reachable. Use an abort to save binary size (see #123356). - rtabort!("something here is badly broken!"); - } - - if let Some(name) = their_thread.cname() { - imp::set_name(name); - } - - let f = f.into_inner(); - let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run()); - crate::sys::backtrace::__rust_begin_short_backtrace(f) - })); - // SAFETY: `their_packet` as been built just above and moved by the - // closure (it is an Arc<...>) and `my_packet` will be stored in the - // same `JoinInner` as this closure meaning the mutation will be - // safe (not modify it and affect a value far away). - unsafe { *their_packet.result.get() = Some(try_result) }; - // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that - // will call `decrement_num_running_threads` and therefore signal that this thread is - // done. - drop(their_packet); - // Here, the lifetime `'scope` can end. `main` keeps running for a bit - // after that before returning itself. - }; - - if let Some(scope_data) = &my_packet.scope { - scope_data.increment_num_running_threads(); - } - - let main = Box::new(main); - // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the - // lifetime change is justified. - let main = - unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + Send + 'static)) }; - - Ok(JoinInner { - // SAFETY: - // - // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed - // through FFI or otherwise used with low-level threading primitives that have no - // notion of or way to enforce lifetimes. - // - // As mentioned in the `Safety` section of this function's documentation, the caller of - // this function needs to guarantee that the passed-in lifetime is sufficiently long - // for the lifetime of the thread. - // - // Similarly, the `sys` implementation must guarantee that no references to the closure - // exist after the thread has terminated, which is signaled by `Thread::join` - // returning. - native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, - thread: my_thread, - packet: my_packet, - }) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Free functions -//////////////////////////////////////////////////////////////////////////////// - -/// Spawns a new thread, returning a [`JoinHandle`] for it. -/// -/// The join handle provides a [`join`] method that can be used to join the spawned -/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing -/// the argument given to [`panic!`]. -/// -/// If the join handle is dropped, the spawned thread will implicitly be *detached*. -/// In this case, the spawned thread may no longer be joined. -/// (It is the responsibility of the program to either eventually join threads it -/// creates or detach them; otherwise, a resource leak will result.) -/// -/// This function creates a thread with the default parameters of [`Builder`]. -/// To specify the new thread's stack size or the name, use [`Builder::spawn`]. -/// -/// As you can see in the signature of `spawn` there are two constraints on -/// both the closure given to `spawn` and its return value, let's explain them: -/// -/// - The `'static` constraint means that the closure and its return value -/// must have a lifetime of the whole program execution. The reason for this -/// is that threads can outlive the lifetime they have been created in. -/// -/// Indeed if the thread, and by extension its return value, can outlive their -/// caller, we need to make sure that they will be valid afterwards, and since -/// we *can't* know when it will return we need to have them valid as long as -/// possible, that is until the end of the program, hence the `'static` -/// lifetime. -/// - The [`Send`] constraint is because the closure will need to be passed -/// *by value* from the thread where it is spawned to the new thread. Its -/// return value will need to be passed from the new thread to the thread -/// where it is `join`ed. -/// As a reminder, the [`Send`] marker trait expresses that it is safe to be -/// passed from thread to thread. [`Sync`] expresses that it is safe to have a -/// reference be passed from thread to thread. -/// -/// # Panics -/// -/// Panics if the OS fails to create a thread; use [`Builder::spawn`] -/// to recover from such errors. -/// -/// # Examples -/// -/// Creating a thread. -/// -/// ``` -/// use std::thread; -/// -/// let handler = thread::spawn(|| { -/// // thread code -/// }); -/// -/// handler.join().unwrap(); -/// ``` -/// -/// As mentioned in the module documentation, threads are usually made to -/// communicate using [`channels`], here is how it usually looks. -/// -/// This example also shows how to use `move`, in order to give ownership -/// of values to a thread. -/// -/// ``` -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// let (tx, rx) = channel(); -/// -/// let sender = thread::spawn(move || { -/// tx.send("Hello, thread".to_owned()) -/// .expect("Unable to send on channel"); -/// }); -/// -/// let receiver = thread::spawn(move || { -/// let value = rx.recv().expect("Unable to receive from channel"); -/// println!("{value}"); -/// }); -/// -/// sender.join().expect("The sender thread has panicked"); -/// receiver.join().expect("The receiver thread has panicked"); -/// ``` -/// -/// A thread can also return a value through its [`JoinHandle`], you can use -/// this to make asynchronous computations (futures might be more appropriate -/// though). -/// -/// ``` -/// use std::thread; -/// -/// let computation = thread::spawn(|| { -/// // Some expensive computation. -/// 42 -/// }); -/// -/// let result = computation.join().unwrap(); -/// println!("{result}"); -/// ``` -/// -/// # Notes -/// -/// This function has the same minimal guarantee regarding "foreign" unwinding operations (e.g. -/// an exception thrown from C++ code, or a `panic!` in Rust code compiled or linked with a -/// different runtime) as [`catch_unwind`]; namely, if the thread created with `thread::spawn` -/// unwinds all the way to the root with such an exception, one of two behaviors are possible, -/// and it is unspecified which will occur: -/// -/// * The process aborts. -/// * The process does not abort, and [`join`] will return a `Result::Err` -/// containing an opaque type. -/// -/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html -/// [`channels`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Err`]: crate::result::Result::Err -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -pub fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(f).expect("failed to spawn thread") -} - -/// Cooperatively gives up a timeslice to the OS scheduler. -/// -/// This calls the underlying OS scheduler's yield primitive, signaling -/// that the calling thread is willing to give up its remaining timeslice -/// so that the OS may schedule other threads on the CPU. -/// -/// A drawback of yielding in a loop is that if the OS does not have any -/// other ready threads to run on the current CPU, the thread will effectively -/// busy-wait, which wastes CPU time and energy. -/// -/// Therefore, when waiting for events of interest, a programmer's first -/// choice should be to use synchronization devices such as [`channel`]s, -/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are -/// implemented in a blocking manner, giving up the CPU until the event -/// of interest has occurred which avoids repeated yielding. -/// -/// `yield_now` should thus be used only rarely, mostly in situations where -/// repeated polling is required because there is no other suitable way to -/// learn when an event of interest has occurred. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// thread::yield_now(); -/// ``` -/// -/// [`channel`]: crate::sync::mpsc -/// [`join`]: JoinHandle::join -/// [`Condvar`]: crate::sync::Condvar -/// [`Mutex`]: crate::sync::Mutex -#[stable(feature = "rust1", since = "1.0.0")] -pub fn yield_now() { - imp::yield_now() -} - -/// Determines whether the current thread is unwinding because of panic. -/// -/// A common use of this feature is to poison shared resources when writing -/// unsafe code, by checking `panicking` when the `drop` is called. -/// -/// This is usually not needed when writing safe code, as [`Mutex`es][Mutex] -/// already poison themselves when a thread panics while holding the lock. -/// -/// This can also be used in multithreaded applications, in order to send a -/// message to other threads warning that a thread has panicked (e.g., for -/// monitoring purposes). -/// -/// # Examples -/// -/// ```should_panic -/// use std::thread; -/// -/// struct SomeStruct; -/// -/// impl Drop for SomeStruct { -/// fn drop(&mut self) { -/// if thread::panicking() { -/// println!("dropped while unwinding"); -/// } else { -/// println!("dropped while not unwinding"); -/// } -/// } -/// } -/// -/// { -/// print!("a: "); -/// let a = SomeStruct; -/// } -/// -/// { -/// print!("b: "); -/// let b = SomeStruct; -/// panic!() -/// } -/// ``` -/// -/// [Mutex]: crate::sync::Mutex -#[inline] -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn panicking() -> bool { - panicking::panicking() -} - -/// Uses [`sleep`]. -/// -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// -/// # Examples -/// -/// ```no_run -/// use std::thread; -/// -/// // Let's sleep for 2 seconds: -/// thread::sleep_ms(2000); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::sleep`")] -pub fn sleep_ms(ms: u32) { - sleep(Duration::from_millis(ms as u64)) -} - -/// Puts the current thread to sleep for at least the specified amount of time. -/// -/// The thread may sleep longer than the duration specified due to scheduling -/// specifics or platform-dependent functionality. It will never sleep less. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// On Unix platforms, the underlying syscall may be interrupted by a -/// spurious wakeup or signal handler. To ensure the sleep occurs for at least -/// the specified duration, this function may invoke that system call multiple -/// times. -/// Platforms which do not support nanosecond precision for sleeping will -/// have `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// Currently, specifying a zero duration on Unix platforms returns immediately -/// without invoking the underlying [`nanosleep`] syscall, whereas on Windows -/// platforms the underlying [`Sleep`] syscall is always invoked. -/// If the intention is to yield the current time-slice you may want to use -/// [`yield_now`] instead. -/// -/// [`nanosleep`]: https://linux.die.net/man/2/nanosleep -/// [`Sleep`]: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep -/// -/// # Examples -/// -/// ```no_run -/// use std::{thread, time}; -/// -/// let ten_millis = time::Duration::from_millis(10); -/// let now = time::Instant::now(); -/// -/// thread::sleep(ten_millis); -/// -/// assert!(now.elapsed() >= ten_millis); -/// ``` -#[stable(feature = "thread_sleep", since = "1.4.0")] -pub fn sleep(dur: Duration) { - imp::sleep(dur) -} - -/// Puts the current thread to sleep until the specified deadline has passed. -/// -/// The thread may still be asleep after the deadline specified due to -/// scheduling specifics or platform-dependent functionality. It will never -/// wake before. -/// -/// This function is blocking, and should not be used in `async` functions. -/// -/// # Platform-specific behavior -/// -/// In most cases this function will call an OS specific function. Where that -/// is not supported [`sleep`] is used. Those platforms are referred to as other -/// in the table below. -/// -/// # Underlying System calls -/// -/// The following system calls are [currently] being used: -/// -/// | Platform | System call | -/// |-----------|----------------------------------------------------------------------| -/// | Linux | [clock_nanosleep] (Monotonic clock) | -/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | -/// | Android | [clock_nanosleep] (Monotonic Clock)] | -/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | -/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | -/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | -/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | -/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] | -/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | -/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | -/// -/// [currently]: crate::io#platform-specific-behavior -/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep -/// -/// **Disclaimer:** These system calls might change over time. -/// -/// # Examples -/// -/// A simple game loop that limits the game to 60 frames per second. -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # fn update() {} -/// # fn render() {} -/// # -/// let max_fps = 60.0; -/// let frame_time = Duration::from_secs_f32(1.0/max_fps); -/// let mut next_frame = Instant::now(); -/// loop { -/// thread::sleep_until(next_frame); -/// next_frame += frame_time; -/// update(); -/// render(); -/// } -/// ``` -/// -/// A slow API we must not call too fast and which takes a few -/// tries before succeeding. By using `sleep_until` the time the -/// API call takes does not influence when we retry or when we give up -/// -/// ```no_run -/// #![feature(thread_sleep_until)] -/// # use std::time::{Duration, Instant}; -/// # use std::thread; -/// # -/// # enum Status { -/// # Ready(usize), -/// # Waiting, -/// # } -/// # fn slow_web_api_call() -> Status { Status::Ready(42) } -/// # -/// # const MAX_DURATION: Duration = Duration::from_secs(10); -/// # -/// # fn try_api_call() -> Result { -/// let deadline = Instant::now() + MAX_DURATION; -/// let delay = Duration::from_millis(250); -/// let mut next_attempt = Instant::now(); -/// loop { -/// if Instant::now() > deadline { -/// break Err(()); -/// } -/// if let Status::Ready(data) = slow_web_api_call() { -/// break Ok(data); -/// } -/// -/// next_attempt = deadline.min(next_attempt + delay); -/// thread::sleep_until(next_attempt); -/// } -/// # } -/// # let _data = try_api_call(); -/// ``` -#[unstable(feature = "thread_sleep_until", issue = "113752")] -pub fn sleep_until(deadline: Instant) { - imp::sleep_until(deadline) -} - -/// Used to ensure that `park` and `park_timeout` do not unwind, as that can -/// cause undefined behavior if not handled correctly (see #102398 for context). -struct PanicGuard; - -impl Drop for PanicGuard { - fn drop(&mut self) { - rtabort!("an irrecoverable error occurred while synchronizing threads") - } -} - -/// Blocks unless or until the current thread's token is made available. -/// -/// A call to `park` does not guarantee that the thread will remain parked -/// forever, and callers should be prepared for this possibility. However, -/// it is guaranteed that this function will not panic (it may abort the -/// process if the implementation encounters some rare errors). -/// -/// # `park` and `unpark` -/// -/// Every thread is equipped with some basic low-level blocking support, via the -/// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] -/// method. [`park`] blocks the current thread, which can then be resumed from -/// another thread by calling the [`unpark`] method on the blocked thread's -/// handle. -/// -/// Conceptually, each [`Thread`] handle has an associated token, which is -/// initially not present: -/// -/// * The [`thread::park`][`park`] function blocks the current thread unless or -/// until the token is available for its thread handle, at which point it -/// atomically consumes the token. It may also return *spuriously*, without -/// consuming the token. [`thread::park_timeout`] does the same, but allows -/// specifying a maximum time to block the thread for. -/// -/// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token can be held by a thread even if it is currently not -/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. -/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens -/// after all `park` that may be done by other data structures! -/// -/// The API is typically used by acquiring a handle to the current thread, placing that handle in a -/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some -/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point -/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it -/// will be woken up properly. -/// -/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread -/// without first establishing that it is about to be `park`ing within your code, that `unpark` may -/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means -/// you must not call unknown code between setting up for parking and calling `park`; for instance, -/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a -/// deadlock. -/// -/// The motivation for this design is twofold: -/// -/// * It avoids the need to allocate mutexes and condvars when building new -/// synchronization primitives; the threads already provide basic -/// blocking/signaling. -/// -/// * It can be implemented very efficiently on many platforms. -/// -/// # Memory Ordering -/// -/// Calls to `unpark` _synchronize-with_ calls to `park`, meaning that memory -/// operations performed before a call to `unpark` are made visible to the thread that -/// consumes the token and returns from `park`. Note that all `park` and `unpark` -/// operations for a given thread form a total order and _all_ prior `unpark` operations -/// synchronize-with `park`. -/// -/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` -/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same -/// thread form a [release sequence]. -/// -/// Note that being unblocked does not imply a call was made to `unpark`, because -/// wakeups can also be spurious. For example, a valid, but inefficient, -/// implementation could have `park` and `unpark` return immediately without doing anything, -/// making *all* wakeups spurious. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// use std::sync::atomic::{Ordering, AtomicBool}; -/// use std::time::Duration; -/// -/// static QUEUED: AtomicBool = AtomicBool::new(false); -/// static FLAG: AtomicBool = AtomicBool::new(false); -/// -/// let parked_thread = thread::spawn(move || { -/// println!("Thread spawned"); -/// // Signal that we are going to `park`. Between this store and our `park`, there may -/// // be no other `park`, or else that `park` could consume our `unpark` token! -/// QUEUED.store(true, Ordering::Release); -/// // We want to wait until the flag is set. We *could* just spin, but using -/// // park/unpark is more efficient. -/// while !FLAG.load(Ordering::Acquire) { -/// // We can *not* use `println!` here since that could use thread parking internally. -/// thread::park(); -/// // We *could* get here spuriously, i.e., way before the 10ms below are over! -/// // But that is no problem, we are in a loop until the flag is set anyway. -/// } -/// println!("Flag received"); -/// }); -/// -/// // Let some time pass for the thread to be spawned. -/// thread::sleep(Duration::from_millis(10)); -/// -/// // Ensure the thread is about to park. -/// // This is crucial! It guarantees that the `unpark` below is not consumed -/// // by some other code in the parked thread (e.g. inside `println!`). -/// while !QUEUED.load(Ordering::Acquire) { -/// // Spinning is of course inefficient; in practice, this would more likely be -/// // a dequeue where we have no work to do if there's nobody queued. -/// std::hint::spin_loop(); -/// } -/// -/// // Set the flag, and let the thread wake up. -/// // There is no race condition here: if `unpark` -/// // happens first, `park` will return immediately. -/// // There is also no other `park` that could consume this token, -/// // since we waited until the other thread got queued. -/// // Hence there is no risk of a deadlock. -/// FLAG.store(true, Ordering::Release); -/// println!("Unpark the thread"); -/// parked_thread.thread().unpark(); -/// -/// parked_thread.join().unwrap(); -/// ``` -/// -/// [`unpark`]: Thread::unpark -/// [`thread::park_timeout`]: park_timeout -/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence -#[stable(feature = "rust1", since = "1.0.0")] -pub fn park() { - let guard = PanicGuard; - // SAFETY: park_timeout is called on the parker owned by this thread. - unsafe { - current().park(); - } - // No panic occurred, do not abort. - forget(guard); -} - -/// Uses [`park_timeout`]. -/// -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that might not cause the maximum -/// amount of time waited to be precisely `ms` long. -/// -/// See the [park documentation][`park`] for more detail. -#[stable(feature = "rust1", since = "1.0.0")] -#[deprecated(since = "1.6.0", note = "replaced by `std::thread::park_timeout`")] -pub fn park_timeout_ms(ms: u32) { - park_timeout(Duration::from_millis(ms as u64)) -} - -/// Blocks unless or until the current thread's token is made available or -/// the specified duration has been reached (may wake spuriously). -/// -/// The semantics of this function are equivalent to [`park`][park] except -/// that the thread will be blocked for roughly no longer than `dur`. This -/// method should not be used for precise timing due to anomalies such as -/// preemption or platform differences that might not cause the maximum -/// amount of time waited to be precisely `dur` long. -/// -/// See the [park documentation][park] for more details. -/// -/// # Platform-specific behavior -/// -/// Platforms which do not support nanosecond precision for sleeping will have -/// `dur` rounded up to the nearest granularity of time they can sleep for. -/// -/// # Examples -/// -/// Waiting for the complete expiration of the timeout: -/// -/// ```rust,no_run -/// use std::thread::park_timeout; -/// use std::time::{Instant, Duration}; -/// -/// let timeout = Duration::from_secs(2); -/// let beginning_park = Instant::now(); -/// -/// let mut timeout_remaining = timeout; -/// loop { -/// park_timeout(timeout_remaining); -/// let elapsed = beginning_park.elapsed(); -/// if elapsed >= timeout { -/// break; -/// } -/// println!("restarting park_timeout after {elapsed:?}"); -/// timeout_remaining = timeout - elapsed; -/// } -/// ``` -#[stable(feature = "park_timeout", since = "1.4.0")] -pub fn park_timeout(dur: Duration) { - let guard = PanicGuard; - // SAFETY: park_timeout is called on a handle owned by this thread. - unsafe { - current().park_timeout(dur); - } - // No panic occurred, do not abort. - forget(guard); -} - -//////////////////////////////////////////////////////////////////////////////// -// ThreadId -//////////////////////////////////////////////////////////////////////////////// - -/// A unique identifier for a running thread. -/// -/// A `ThreadId` is an opaque object that uniquely identifies each thread -/// created during the lifetime of a process. `ThreadId`s are guaranteed not to -/// be reused, even when a thread terminates. `ThreadId`s are under the control -/// of Rust's standard library and there may not be any relationship between -/// `ThreadId` and the underlying platform's notion of a thread identifier -- -/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId` -/// can be retrieved from the [`id`] method on a [`Thread`]. -/// -/// # Examples -/// -/// ``` -/// use std::thread; -/// -/// let other_thread = thread::spawn(|| { -/// thread::current().id() -/// }); -/// -/// let other_thread_id = other_thread.join().unwrap(); -/// assert!(thread::current().id() != other_thread_id); -/// ``` -/// -/// [`id`]: Thread::id -#[stable(feature = "thread_id", since = "1.19.0")] -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -pub struct ThreadId(NonZero); - -impl ThreadId { - // Generate a new unique thread ID. - pub(crate) fn new() -> ThreadId { - #[cold] - fn exhausted() -> ! { - panic!("failed to generate unique thread ID: bitspace exhausted") - } - - cfg_select! { - target_has_atomic = "64" => { - use crate::sync::atomic::{Atomic, AtomicU64}; - - static COUNTER: Atomic = AtomicU64::new(0); - - let mut last = COUNTER.load(Ordering::Relaxed); - loop { - let Some(id) = last.checked_add(1) else { - exhausted(); - }; - - match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - Ok(_) => return ThreadId(NonZero::new(id).unwrap()), - Err(id) => last = id, - } - } - } - _ => { - use crate::sync::{Mutex, PoisonError}; - - static COUNTER: Mutex = Mutex::new(0); - - let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); - let Some(id) = counter.checked_add(1) else { - // in case the panic handler ends up calling `ThreadId::new()`, - // avoid reentrant lock acquire. - drop(counter); - exhausted(); - }; - - *counter = id; - drop(counter); - ThreadId(NonZero::new(id).unwrap()) - } - } - } - - #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] - fn from_u64(v: u64) -> Option { - NonZero::new(v).map(ThreadId) - } - - /// This returns a numeric identifier for the thread identified by this - /// `ThreadId`. - /// - /// As noted in the documentation for the type itself, it is essentially an - /// opaque ID, but is guaranteed to be unique for each thread. The returned - /// value is entirely opaque -- only equality testing is stable. Note that - /// it is not guaranteed which values new threads will return, and this may - /// change across Rust versions. - #[must_use] - #[unstable(feature = "thread_id_value", issue = "67939")] - pub fn as_u64(&self) -> NonZero { - self.0 - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Thread -//////////////////////////////////////////////////////////////////////////////// - -// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. -mod thread_name_string { - 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 From for ThreadNameString { - fn from(s: String) -> Self { - Self { - inner: CString::new(s).expect("thread name may not contain interior null bytes"), - } - } - } - - impl ThreadNameString { - pub fn as_cstr(&self) -> &CStr { - &self.inner - } - - pub fn as_str(&self) -> &str { - // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } -} - -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_select! { - target_has_atomic = "64" => { - use super::ThreadId; - use crate::sync::atomic::{Atomic, AtomicU64}; - use crate::sync::atomic::Ordering::Relaxed; - - static MAIN: Atomic = AtomicU64::new(0); - - pub(super) fn get() -> Option { - ThreadId::from_u64(MAIN.load(Relaxed)) - } - - /// # Safety - /// May only be called once. - pub(crate) unsafe fn set(id: ThreadId) { - MAIN.store(id.as_u64().get(), Relaxed) - } - } - _ => { - use super::ThreadId; - use crate::mem::MaybeUninit; - use crate::sync::atomic::{Atomic, AtomicBool}; - use crate::sync::atomic::Ordering::{Acquire, Release}; - - static INIT: Atomic = AtomicBool::new(false); - static mut MAIN: MaybeUninit = MaybeUninit::uninit(); - - pub(super) fn get() -> Option { - 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); - } - } - } -} - -/// 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: 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 -/// -/// We explicitly set the alignment for our guarantee in Thread::into_raw. This -/// allows applications to stuff extra metadata bits into the alignment, which -/// can be rather useful when working with atomics. -#[repr(align(8))] -struct Inner { - name: Option, - id: ThreadId, - parker: Parker, -} - -impl Inner { - fn parker(self: Pin<&Self>) -> Pin<&Parker> { - unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } - } -} - -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -/// A handle to a thread. -/// -/// Threads are represented via the `Thread` type, which you can get in one of -/// two ways: -/// -/// * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] -/// function, and calling [`thread`][`JoinHandle::thread`] on the -/// [`JoinHandle`]. -/// * By requesting the current thread, using the [`thread::current`] function. -/// -/// The [`thread::current`] function is available even for threads not spawned -/// by the APIs of this module. -/// -/// There is usually no need to create a `Thread` struct yourself, one -/// should instead use a function like `spawn` to create new threads, see the -/// docs of [`Builder`] and [`spawn`] for more details. -/// -/// [`thread::current`]: current::current -pub struct Thread { - inner: Pin>, -} - -impl Thread { - pub(crate) fn new(id: ThreadId, name: Option) -> Thread { - let name = name.map(ThreadNameString::from); - - // 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::::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); - Parker::new_in_place(&raw mut (*ptr).parker); - Pin::new_unchecked(arc.assume_init()) - }; - - Thread { inner } - } - - /// Like the public [`park`], but callable on any handle. This is used to - /// allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - pub(crate) unsafe fn park(&self) { - unsafe { self.inner.as_ref().parker().park() } - } - - /// Like the public [`park_timeout`], but callable on any handle. This is - /// used to allow parking in TLS destructors. - /// - /// # Safety - /// May only be called from the thread to which this handle belongs. - pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.inner.as_ref().parker().park_timeout(dur) } - } - - /// Atomically makes the handle's token available if it is not already. - /// - /// Every thread is equipped with some basic low-level blocking support, via - /// the [`park`][park] function and the `unpark()` method. These can be - /// used as a more CPU-efficient implementation of a spinlock. - /// - /// See the [park documentation][park] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::atomic::{AtomicBool, Ordering}; - /// - /// static QUEUED: AtomicBool = AtomicBool::new(false); - /// - /// let parked_thread = thread::Builder::new() - /// .spawn(|| { - /// println!("Parking thread"); - /// QUEUED.store(true, Ordering::Release); - /// thread::park(); - /// println!("Thread unparked"); - /// }) - /// .unwrap(); - /// - /// // Let some time pass for the thread to be spawned. - /// thread::sleep(Duration::from_millis(10)); - /// - /// // Wait until the other thread is queued. - /// // This is crucial! It guarantees that the `unpark` below is not consumed - /// // by some other code in the parked thread (e.g. inside `println!`). - /// while !QUEUED.load(Ordering::Acquire) { - /// // Spinning is of course inefficient; in practice, this would more likely be - /// // a dequeue where we have no work to do if there's nobody queued. - /// std::hint::spin_loop(); - /// } - /// - /// println!("Unpark the thread"); - /// parked_thread.thread().unpark(); - /// - /// parked_thread.join().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn unpark(&self) { - self.inner.as_ref().parker().unpark(); - } - - /// Gets the thread's unique identifier. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let other_thread = thread::spawn(|| { - /// thread::current().id() - /// }); - /// - /// let other_thread_id = other_thread.join().unwrap(); - /// assert!(thread::current().id() != other_thread_id); - /// ``` - #[stable(feature = "thread_id", since = "1.19.0")] - #[must_use] - pub fn id(&self) -> ThreadId { - self.inner.id - } - - /// Gets the thread's name. - /// - /// For more information about named threads, see - /// [this module-level documentation][naming-threads]. - /// - /// # Examples - /// - /// Threads by default have no name specified: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let handler = builder.spawn(|| { - /// assert!(thread::current().name().is_none()); - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// Thread with a specified name: - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new() - /// .name("foo".into()); - /// - /// let handler = builder.spawn(|| { - /// assert_eq!(thread::current().name(), Some("foo")) - /// }).unwrap(); - /// - /// handler.join().unwrap(); - /// ``` - /// - /// [naming-threads]: ./index.html#naming-threads - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn name(&self) -> Option<&str> { - 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. - /// - /// To avoid a memory leak the pointer must be converted - /// back into a `Thread` using [`Thread::from_raw`]. The pointer is - /// guaranteed to be aligned to at least 8 bytes. - /// - /// # Examples - /// - /// ``` - /// #![feature(thread_raw)] - /// - /// use std::thread::{self, Thread}; - /// - /// let thread = thread::current(); - /// let id = thread.id(); - /// let ptr = Thread::into_raw(thread); - /// unsafe { - /// assert_eq!(Thread::from_raw(ptr).id(), id); - /// } - /// ``` - #[unstable(feature = "thread_raw", issue = "97523")] - pub fn into_raw(self) -> *const () { - // 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. - /// - /// The raw pointer must have been previously returned - /// by a call to [`Thread::into_raw`]. - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead - /// to memory unsafety, even if the returned `Thread` is never - /// accessed. - /// - /// Creating a `Thread` from a pointer other than one returned - /// from [`Thread::into_raw`] is **undefined behavior**. - /// - /// Calling this function twice on the same raw pointer can lead - /// to a double-free if both `Thread` instances are dropped. - #[unstable(feature = "thread_raw", issue = "97523")] - pub unsafe fn from_raw(ptr: *const ()) -> Thread { - // Safety: Upheld by caller. - 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 - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Thread { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Thread") - .field("id", &self.id()) - .field("name", &self.name()) - .finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// JoinHandle -//////////////////////////////////////////////////////////////////////////////// - /// A specialized [`Result`] type for threads. /// /// Indicates the manner in which a thread exited. @@ -1745,353 +261,8 @@ impl fmt::Debug for Thread { #[doc(search_unbox)] pub type Result = crate::result::Result>; -// This packet is used to communicate the return value between the spawned -// thread and the rest of the program. It is shared through an `Arc` and -// there's no need for a mutex here because synchronization happens with `join()` -// (the caller will never read this packet until the thread has exited). -// -// An Arc to the packet is stored into a `JoinInner` which in turns is placed -// in `JoinHandle`. -struct Packet<'scope, T> { - scope: Option>, - result: UnsafeCell>>, - _marker: PhantomData>, -} - -// Due to the usage of `UnsafeCell` we need to manually implement Sync. -// The type `T` should already always be Send (otherwise the thread could not -// have been created) and the Packet is Sync because all access to the -// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. -unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} - -impl<'scope, T> Drop for Packet<'scope, T> { - fn drop(&mut self) { - // If this packet was for a thread that ran in a scope, the thread - // panicked, and nobody consumed the panic payload, we make sure - // the scope function will panic. - let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); - // Drop the result without causing unwinding. - // This is only relevant for threads that aren't join()ed, as - // join() will take the `result` and set it to None, such that - // there is nothing left to drop here. - // If this panics, we should handle that, because we're outside the - // outermost `catch_unwind` of our thread. - // We just abort in that case, since there's nothing else we can do. - // (And even if we tried to handle it somehow, we'd also need to handle - // the case where the panic payload we get out of it also panics on - // drop, and so on. See issue #86027.) - if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { - *self.result.get_mut() = None; - })) { - rtabort!("thread result panicked on drop"); - } - // Book-keeping so the scope knows when it's done. - if let Some(scope) = &self.scope { - // Now that there will be no more user code running on this thread - // that can use 'scope, mark the thread as 'finished'. - // It's important we only do this after the `result` has been dropped, - // since dropping it might still use things it borrowed from 'scope. - scope.decrement_num_running_threads(unhandled_panic); - } - } -} - -/// Inner representation for JoinHandle -struct JoinInner<'scope, T> { - native: imp::Thread, - thread: Thread, - packet: Arc>, -} - -impl<'scope, T> JoinInner<'scope, T> { - fn join(mut self) -> Result { - self.native.join(); - Arc::get_mut(&mut self.packet) - // FIXME(fuzzypixelz): returning an error instead of panicking here - // would require updating the documentation of - // `std::thread::Result`; currently we can return `Err` if and only - // if the thread had panicked. - .expect("threads should not terminate unexpectedly") - .result - .get_mut() - .take() - .unwrap() - } -} - -/// An owned permission to join on a thread (block on its termination). -/// -/// A `JoinHandle` *detaches* the associated thread when it is dropped, which -/// means that there is no longer any handle to the thread and no way to `join` -/// on it. -/// -/// Due to platform restrictions, it is not possible to [`Clone`] this -/// handle: the ability to join a thread is a uniquely-owned permission. -/// -/// This `struct` is created by the [`thread::spawn`] function and the -/// [`thread::Builder::spawn`] method. -/// -/// # Examples -/// -/// Creation from [`thread::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { -/// // some work here -/// }); -/// ``` -/// -/// Creation from [`thread::Builder::spawn`]: -/// -/// ``` -/// use std::thread; -/// -/// let builder = thread::Builder::new(); -/// -/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { -/// // some work here -/// }).unwrap(); -/// ``` -/// -/// A thread being detached and outliving the thread that spawned it: -/// -/// ```no_run -/// use std::thread; -/// use std::time::Duration; -/// -/// let original_thread = thread::spawn(|| { -/// let _detached_thread = thread::spawn(|| { -/// // Here we sleep to make sure that the first thread returns before. -/// thread::sleep(Duration::from_millis(10)); -/// // This will be called, even though the JoinHandle is dropped. -/// println!("♫ Still alive ♫"); -/// }); -/// }); -/// -/// original_thread.join().expect("The thread being joined has panicked"); -/// println!("Original thread is joined."); -/// -/// // We make sure that the new thread has time to run, before the main -/// // thread returns. -/// -/// thread::sleep(Duration::from_millis(1000)); -/// ``` -/// -/// [`thread::Builder::spawn`]: Builder::spawn -/// [`thread::spawn`]: spawn -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(target_os = "teeos", must_use)] -pub struct JoinHandle(JoinInner<'static, T>); - -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Send for JoinHandle {} -#[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")] -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - /// Extracts a handle to the underlying thread. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// - /// let thread = join_handle.thread(); - /// println!("thread id: {:?}", thread.id()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - pub fn thread(&self) -> &Thread { - &self.0.thread - } - - /// Waits for the associated thread to finish. - /// - /// This function will return immediately if the associated thread has already finished. - /// - /// In terms of [atomic memory orderings], the completion of the associated - /// thread synchronizes with this function returning. In other words, all - /// operations performed by that thread [happen - /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all - /// operations that happen after `join` returns. - /// - /// If the associated thread panics, [`Err`] is returned with the parameter given - /// to [`panic!`] (though see the Notes below). - /// - /// [`Err`]: crate::result::Result::Err - /// [atomic memory orderings]: crate::sync::atomic - /// - /// # Panics - /// - /// This function may panic on some platforms if a thread attempts to join - /// itself or otherwise may create a deadlock with joining threads. - /// - /// # Examples - /// - /// ``` - /// use std::thread; - /// - /// let builder = thread::Builder::new(); - /// - /// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { - /// // some work here - /// }).unwrap(); - /// join_handle.join().expect("Couldn't join on the associated thread"); - /// ``` - /// - /// # Notes - /// - /// If a "foreign" unwinding operation (e.g. an exception thrown from C++ - /// code, or a `panic!` in Rust code compiled or linked with a different - /// runtime) unwinds all the way to the thread root, the process may be - /// aborted; see the Notes on [`thread::spawn`]. If the process is not - /// aborted, this function will return a `Result::Err` containing an opaque - /// type. - /// - /// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html - /// [`thread::spawn`]: spawn - #[stable(feature = "rust1", since = "1.0.0")] - pub fn join(self) -> Result { - self.0.join() - } - - /// Checks if the associated thread has finished running its main function. - /// - /// `is_finished` supports implementing a non-blocking join operation, by checking - /// `is_finished`, and calling `join` if it returns `true`. This function does not block. To - /// block while waiting on the thread to finish, use [`join`][Self::join]. - /// - /// This might return `true` for a brief moment after the thread's main - /// function has returned, but before the thread itself has stopped running. - /// However, once this returns `true`, [`join`][Self::join] can be expected - /// to return quickly, without blocking for any significant amount of time. - #[stable(feature = "thread_is_running", since = "1.61.0")] - pub fn is_finished(&self) -> bool { - Arc::strong_count(&self.0.packet) == 1 - } -} - -impl AsInner for JoinHandle { - fn as_inner(&self) -> &imp::Thread { - &self.0.native - } -} - -impl IntoInner for JoinHandle { - fn into_inner(self) -> imp::Thread { - self.0.native - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for JoinHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("JoinHandle").finish_non_exhaustive() - } -} - fn _assert_sync_and_send() { fn _assert_both() {} _assert_both::>(); _assert_both::(); } - -/// Returns an estimate of the default amount of parallelism a program should use. -/// -/// Parallelism is a resource. A given machine provides a certain capacity for -/// parallelism, i.e., a bound on the number of computations it can perform -/// simultaneously. This number often corresponds to the amount of CPUs a -/// computer has, but it may diverge in various cases. -/// -/// Host environments such as VMs or container orchestrators may want to -/// restrict the amount of parallelism made available to programs in them. This -/// is often done to limit the potential impact of (unintentionally) -/// resource-intensive programs on other programs running on the same machine. -/// -/// # Limitations -/// -/// The purpose of this API is to provide an easy and portable way to query -/// the default amount of parallelism the program should use. Among other things it -/// does not expose information on NUMA regions, does not account for -/// differences in (co)processor capabilities or current system load, -/// and will not modify the program's global state in order to more accurately -/// query the amount of available parallelism. -/// -/// Where both fixed steady-state and burst limits are available the steady-state -/// capacity will be used to ensure more predictable latencies. -/// -/// Resource limits can be changed during the runtime of a program, therefore the value is -/// not cached and instead recomputed every time this function is called. It should not be -/// called from hot code. -/// -/// The value returned by this function should be considered a simplified -/// approximation of the actual amount of parallelism available at any given -/// time. To get a more detailed or precise overview of the amount of -/// parallelism available to the program, you may wish to use -/// platform-specific APIs as well. The following platform limitations currently -/// apply to `available_parallelism`: -/// -/// On Windows: -/// - It may undercount the amount of parallelism available on systems with more -/// than 64 logical CPUs. However, programs typically need specific support to -/// take advantage of more than 64 logical CPUs, and in the absence of such -/// support, the number returned by this function accurately reflects the -/// number of logical CPUs the program can use by default. -/// - It may overcount the amount of parallelism available on systems limited by -/// process-wide affinity masks, or job object limitations. -/// -/// On Linux: -/// - It may overcount the amount of parallelism available when limited by a -/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be -/// queried, e.g. due to sandboxing. -/// - It may undercount the amount of parallelism if the current thread's affinity mask -/// does not reflect the process' cpuset, e.g. due to pinned threads. -/// - If the process is in a cgroup v1 cpu controller, this may need to -/// scan mountpoints to find the corresponding cgroup v1 controller, -/// which may take time on systems with large numbers of mountpoints. -/// (This does not apply to cgroup v2, or to processes not in a -/// cgroup.) -/// - It does not attempt to take `ulimit` into account. If there is a limit set on the number of -/// threads, `available_parallelism` cannot know how much of that limit a Rust program should -/// take, or know in a reliable and race-free way how much of that limit is already taken. -/// -/// On all targets: -/// - It may overcount the amount of parallelism available when running in a VM -/// with CPU usage limits (e.g. an overcommitted host). -/// -/// # Errors -/// -/// This function will, but is not limited to, return errors in the following -/// cases: -/// -/// - If the amount of parallelism is not known for the target platform. -/// - If the program lacks permission to query the amount of parallelism made -/// available to it. -/// -/// # Examples -/// -/// ``` -/// # #![allow(dead_code)] -/// use std::{io, thread}; -/// -/// fn main() -> io::Result<()> { -/// let count = thread::available_parallelism()?.get(); -/// assert!(count >= 1_usize); -/// Ok(()) -/// } -/// ``` -#[doc(alias = "available_concurrency")] // Alias for a previous name we gave this API on unstable. -#[doc(alias = "hardware_concurrency")] // Alias for C++ `std::thread::hardware_concurrency`. -#[doc(alias = "num_cpus")] // Alias for a popular ecosystem crate which provides similar functionality. -#[stable(feature = "available_parallelism", since = "1.59.0")] -pub fn available_parallelism() -> io::Result> { - imp::available_parallelism() -} diff --git a/std/src/thread/scoped.rs b/std/src/thread/scoped.rs index 75a5303fc321a..301f5e949cac3 100644 --- a/std/src/thread/scoped.rs +++ b/std/src/thread/scoped.rs @@ -1,4 +1,8 @@ -use super::{Builder, JoinInner, Result, Thread, current_or_unnamed}; +use super::Result; +use super::builder::Builder; +use super::current::current_or_unnamed; +use super::lifecycle::{JoinInner, spawn_unchecked}; +use super::thread::Thread; use crate::marker::PhantomData; use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::sync::Arc; @@ -257,7 +261,10 @@ impl Builder { F: FnOnce() -> T + Send + 'scope, T: Send + 'scope, { - Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(scope.data.clone())) }?)) + let Builder { name, stack_size, no_hooks } = self; + Ok(ScopedJoinHandle(unsafe { + spawn_unchecked(name, stack_size, no_hooks, Some(scope.data.clone()), f) + }?)) } } @@ -279,7 +286,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { #[must_use] #[stable(feature = "scoped_threads", since = "1.63.0")] pub fn thread(&self) -> &Thread { - &self.0.thread + self.0.thread() } /// Waits for the associated thread to finish. @@ -325,7 +332,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { /// to return quickly, without blocking for any significant amount of time. #[stable(feature = "scoped_threads", since = "1.63.0")] pub fn is_finished(&self) -> bool { - Arc::strong_count(&self.0.packet) == 1 + self.0.is_finished() } } diff --git a/std/src/thread/spawnhook.rs b/std/src/thread/spawnhook.rs index c8a7bcf55c14e..254793ac33d08 100644 --- a/std/src/thread/spawnhook.rs +++ b/std/src/thread/spawnhook.rs @@ -1,7 +1,7 @@ +use super::thread::Thread; use crate::cell::Cell; use crate::iter; use crate::sync::Arc; -use crate::thread::Thread; crate::thread_local! { /// A thread local linked list of spawn hooks. diff --git a/std/src/thread/tests.rs b/std/src/thread/tests.rs index 2117f5f93ce26..4b934c039a36f 100644 --- a/std/src/thread/tests.rs +++ b/std/src/thread/tests.rs @@ -1,11 +1,10 @@ -use super::Builder; use crate::any::Any; use crate::panic::panic_any; use crate::result; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::mpsc::{Sender, channel}; use crate::sync::{Arc, Barrier}; -use crate::thread::{self, Scope, ThreadId}; +use crate::thread::{self, Builder, Scope, ThreadId}; use crate::time::{Duration, Instant}; // !!! These tests are dangerous. If something is buggy, they will hang, !!! diff --git a/std/src/thread/thread.rs b/std/src/thread/thread.rs new file mode 100644 index 0000000000000..7c9c91c3b0c78 --- /dev/null +++ b/std/src/thread/thread.rs @@ -0,0 +1,326 @@ +use super::id::ThreadId; +use super::main_thread; +use crate::alloc::System; +use crate::ffi::CStr; +use crate::fmt; +use crate::pin::Pin; +use crate::sync::Arc; +use crate::sys::sync::Parker; +use crate::time::Duration; + +// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. +mod thread_name_string { + 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 From for ThreadNameString { + fn from(s: String) -> Self { + Self { + inner: CString::new(s).expect("thread name may not contain interior null bytes"), + } + } + } + + impl ThreadNameString { + pub fn as_cstr(&self) -> &CStr { + &self.inner + } + + pub fn as_str(&self) -> &str { + // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. + unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } + } + } +} + +use thread_name_string::ThreadNameString; + +/// The internal representation of a `Thread` handle +/// +/// We explicitly set the alignment for our guarantee in Thread::into_raw. This +/// allows applications to stuff extra metadata bits into the alignment, which +/// can be rather useful when working with atomics. +#[repr(align(8))] +struct Inner { + name: Option, + id: ThreadId, + parker: Parker, +} + +impl Inner { + fn parker(self: Pin<&Self>) -> Pin<&Parker> { + unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } + } +} + +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +/// A handle to a thread. +/// +/// Threads are represented via the `Thread` type, which you can get in one of +/// two ways: +/// +/// * By spawning a new thread, e.g., using the [`thread::spawn`] +/// function, and calling [`thread`] on the [`JoinHandle`]. +/// * By requesting the current thread, using the [`thread::current`] function. +/// +/// The [`thread::current`] function is available even for threads not spawned +/// by the APIs of this module. +/// +/// There is usually no need to create a `Thread` struct yourself, one +/// should instead use a function like `spawn` to create new threads, see the +/// docs of [`Builder`] and [`spawn`] for more details. +/// +/// [`thread::spawn`]: super::spawn +/// [`thread`]: super::JoinHandle::thread +/// [`JoinHandle`]: super::JoinHandle +/// [`thread::current`]: super::current::current +/// [`Builder`]: super::Builder +/// [`spawn`]: super::spawn +pub struct Thread { + // We use the System allocator such that creating or dropping this handle + // does not interfere with a potential Global allocator using thread-local + // storage. + inner: Pin>, +} + +impl Thread { + pub(crate) fn new(id: ThreadId, name: Option) -> Thread { + let name = name.map(ThreadNameString::from); + + // 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::::new_uninit_in(System); + let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); + (&raw mut (*ptr).name).write(name); + (&raw mut (*ptr).id).write(id); + Parker::new_in_place(&raw mut (*ptr).parker); + Pin::new_unchecked(arc.assume_init()) + }; + + Thread { inner } + } + + /// Like the public [`park`], but callable on any handle. This is used to + /// allow parking in TLS destructors. + /// + /// # Safety + /// May only be called from the thread to which this handle belongs. + /// + /// [`park`]: super::park + pub(crate) unsafe fn park(&self) { + unsafe { self.inner.as_ref().parker().park() } + } + + /// Like the public [`park_timeout`], but callable on any handle. This is + /// used to allow parking in TLS destructors. + /// + /// # Safety + /// May only be called from the thread to which this handle belongs. + /// + /// [`park_timeout`]: super::park_timeout + pub(crate) unsafe fn park_timeout(&self, dur: Duration) { + unsafe { self.inner.as_ref().parker().park_timeout(dur) } + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// Every thread is equipped with some basic low-level blocking support, via + /// the [`park`] function and the `unpark()` method. These can be used as a + /// more CPU-efficient implementation of a spinlock. + /// + /// See the [park documentation] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// static QUEUED: AtomicBool = AtomicBool::new(false); + /// + /// let parked_thread = thread::Builder::new() + /// .spawn(|| { + /// println!("Parking thread"); + /// QUEUED.store(true, Ordering::Release); + /// thread::park(); + /// println!("Thread unparked"); + /// }) + /// .unwrap(); + /// + /// // Let some time pass for the thread to be spawned. + /// thread::sleep(Duration::from_millis(10)); + /// + /// // Wait until the other thread is queued. + /// // This is crucial! It guarantees that the `unpark` below is not consumed + /// // by some other code in the parked thread (e.g. inside `println!`). + /// while !QUEUED.load(Ordering::Acquire) { + /// // Spinning is of course inefficient; in practice, this would more likely be + /// // a dequeue where we have no work to do if there's nobody queued. + /// std::hint::spin_loop(); + /// } + /// + /// println!("Unpark the thread"); + /// parked_thread.thread().unpark(); + /// + /// parked_thread.join().unwrap(); + /// ``` + /// + /// [`park`]: super::park + /// [park documentation]: super::park + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn unpark(&self) { + self.inner.as_ref().parker().unpark(); + } + + /// Gets the thread's unique identifier. + /// + /// # Examples + /// + /// ``` + /// use std::thread; + /// + /// let other_thread = thread::spawn(|| { + /// thread::current().id() + /// }); + /// + /// let other_thread_id = other_thread.join().unwrap(); + /// assert!(thread::current().id() != other_thread_id); + /// ``` + #[stable(feature = "thread_id", since = "1.19.0")] + #[must_use] + pub fn id(&self) -> ThreadId { + self.inner.id + } + + /// Gets the thread's name. + /// + /// For more information about named threads, see + /// [this module-level documentation][naming-threads]. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// [naming-threads]: ./index.html#naming-threads + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + pub fn name(&self) -> Option<&str> { + 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. + /// + /// To avoid a memory leak the pointer must be converted + /// back into a `Thread` using [`Thread::from_raw`]. The pointer is + /// guaranteed to be aligned to at least 8 bytes. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_raw)] + /// + /// use std::thread::{self, Thread}; + /// + /// let thread = thread::current(); + /// let id = thread.id(); + /// let ptr = Thread::into_raw(thread); + /// unsafe { + /// assert_eq!(Thread::from_raw(ptr).id(), id); + /// } + /// ``` + #[unstable(feature = "thread_raw", issue = "97523")] + pub fn into_raw(self) -> *const () { + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw_with_allocator(inner).0 as *const () + } + + /// Constructs a `Thread` from a raw pointer. + /// + /// The raw pointer must have been previously returned + /// by a call to [`Thread::into_raw`]. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead + /// to memory unsafety, even if the returned `Thread` is never + /// accessed. + /// + /// Creating a `Thread` from a pointer other than one returned + /// from [`Thread::into_raw`] is **undefined behavior**. + /// + /// Calling this function twice on the same raw pointer can lead + /// to a double-free if both `Thread` instances are dropped. + #[unstable(feature = "thread_raw", issue = "97523")] + pub unsafe fn from_raw(ptr: *const ()) -> Thread { + // Safety: Upheld by caller. + unsafe { + Thread { inner: Pin::new_unchecked(Arc::from_raw_in(ptr as *const Inner, System)) } + } + } + + pub(crate) 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 + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Thread { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Thread") + .field("id", &self.id()) + .field("name", &self.name()) + .finish_non_exhaustive() + } +} diff --git a/std/tests/path.rs b/std/tests/path.rs index c60edbdf961e1..4094b7acd8749 100644 --- a/std/tests/path.rs +++ b/std/tests/path.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![feature(clone_to_uninit)] -#![feature(maybe_uninit_slice)] #![feature(normalize_lexically)] #![feature(path_trailing_sep)] // tidy-alphabetical-end