summaryrefslogtreecommitdiffstats
path: root/library/std
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
commit1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch)
tree3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /library/std
parentReleasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz
rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std')
-rw-r--r--library/std/Cargo.toml4
-rw-r--r--library/std/build.rs3
-rw-r--r--library/std/src/collections/hash/map.rs1
-rw-r--r--library/std/src/collections/hash/set.rs8
-rw-r--r--library/std/src/collections/mod.rs11
-rw-r--r--library/std/src/env.rs18
-rw-r--r--library/std/src/f32.rs36
-rw-r--r--library/std/src/f32/tests.rs16
-rw-r--r--library/std/src/f64.rs36
-rw-r--r--library/std/src/f64/tests.rs16
-rw-r--r--library/std/src/ffi/os_str.rs1
-rw-r--r--library/std/src/fs/tests.rs20
-rw-r--r--library/std/src/io/buffered/bufwriter.rs2
-rw-r--r--library/std/src/io/cursor.rs2
-rw-r--r--library/std/src/io/error.rs13
-rw-r--r--library/std/src/io/mod.rs20
-rw-r--r--library/std/src/io/stdio.rs27
-rw-r--r--library/std/src/keyword_docs.rs2
-rw-r--r--library/std/src/lib.rs31
-rw-r--r--library/std/src/net/tcp.rs12
-rw-r--r--library/std/src/net/tcp/tests.rs28
-rw-r--r--library/std/src/os/android/net.rs4
-rw-r--r--library/std/src/os/fd/owned.rs4
-rw-r--r--library/std/src/os/linux/net.rs4
-rw-r--r--library/std/src/os/linux/raw.rs1
-rw-r--r--library/std/src/os/net/linux_ext/addr.rs6
-rw-r--r--library/std/src/os/net/linux_ext/mod.rs2
-rw-r--r--library/std/src/os/unix/net/addr.rs4
-rw-r--r--library/std/src/os/unix/net/ancillary.rs141
-rw-r--r--library/std/src/os/unix/net/datagram.rs45
-rw-r--r--library/std/src/os/unix/net/listener.rs3
-rw-r--r--library/std/src/os/unix/net/stream.rs39
-rw-r--r--library/std/src/os/unix/net/tests.rs2
-rw-r--r--library/std/src/os/windows/io/handle.rs2
-rw-r--r--library/std/src/panic.rs3
-rw-r--r--library/std/src/panicking.rs16
-rw-r--r--library/std/src/path.rs48
-rw-r--r--library/std/src/personality/gcc.rs3
-rw-r--r--library/std/src/prelude/mod.rs6
-rw-r--r--library/std/src/primitive_docs.rs84
-rw-r--r--library/std/src/process.rs86
-rw-r--r--library/std/src/process/tests.rs34
-rw-r--r--library/std/src/sync/lazy_lock.rs24
-rw-r--r--library/std/src/sync/mod.rs9
-rw-r--r--library/std/src/sync/mpmc/array.rs107
-rw-r--r--library/std/src/sync/mpmc/list.rs12
-rw-r--r--library/std/src/sync/mpmc/mod.rs4
-rw-r--r--library/std/src/sync/mpsc/sync_tests.rs13
-rw-r--r--library/std/src/sync/mutex.rs24
-rw-r--r--library/std/src/sync/once_lock.rs59
-rw-r--r--library/std/src/sync/remutex.rs2
-rw-r--r--library/std/src/sys/common/alloc.rs1
-rw-r--r--library/std/src/sys/common/mod.rs1
-rw-r--r--library/std/src/sys/common/thread_local/fast_local.rs254
-rw-r--r--library/std/src/sys/common/thread_local/mod.rs109
-rw-r--r--library/std/src/sys/common/thread_local/os_local.rs197
-rw-r--r--library/std/src/sys/common/thread_local/static_local.rs115
-rw-r--r--library/std/src/sys/hermit/fs.rs2
-rw-r--r--library/std/src/sys/hermit/futex.rs6
-rw-r--r--library/std/src/sys/hermit/mod.rs14
-rw-r--r--library/std/src/sys/hermit/net.rs31
-rw-r--r--library/std/src/sys/mod.rs9
-rw-r--r--library/std/src/sys/sgx/abi/usercalls/alloc.rs1
-rw-r--r--library/std/src/sys/sgx/fd.rs6
-rw-r--r--library/std/src/sys/sgx/net.rs6
-rw-r--r--library/std/src/sys/solid/net.rs23
-rw-r--r--library/std/src/sys/unix/fd.rs9
-rw-r--r--library/std/src/sys/unix/fs.rs9
-rw-r--r--library/std/src/sys/unix/futex.rs2
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs62
-rw-r--r--library/std/src/sys/unix/kernel_copy/tests.rs42
-rw-r--r--library/std/src/sys/unix/net.rs39
-rw-r--r--library/std/src/sys/unix/os.rs6
-rw-r--r--library/std/src/sys/unix/pipe.rs6
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs2
-rw-r--r--library/std/src/sys/unix/rand.rs5
-rw-r--r--library/std/src/sys/unix/stdio.rs6
-rw-r--r--library/std/src/sys/unix/time.rs50
-rw-r--r--library/std/src/sys/unsupported/net.rs6
-rw-r--r--library/std/src/sys/unsupported/pipe.rs6
-rw-r--r--library/std/src/sys/wasi/fd.rs18
-rw-r--r--library/std/src/sys/wasi/fs.rs2
-rw-r--r--library/std/src/sys/wasi/net.rs6
-rw-r--r--library/std/src/sys/windows/args.rs23
-rw-r--r--library/std/src/sys/windows/c.rs87
-rw-r--r--library/std/src/sys/windows/c/errors.rs2
-rw-r--r--library/std/src/sys/windows/fs.rs46
-rw-r--r--library/std/src/sys/windows/handle.rs9
-rw-r--r--library/std/src/sys/windows/net.rs28
-rw-r--r--library/std/src/sys/windows/path.rs65
-rw-r--r--library/std/src/sys/windows/pipe.rs24
-rw-r--r--library/std/src/sys/windows/process.rs12
-rw-r--r--library/std/src/sys_common/net.rs7
-rw-r--r--library/std/src/sys_common/thread_parking/id.rs2
-rw-r--r--library/std/src/thread/local.rs567
-rw-r--r--library/std/src/thread/mod.rs40
96 files changed, 1991 insertions, 1070 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 598a4bf92..96c75f97f 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -15,8 +15,8 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
-libc = { version = "0.2.139", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.87" }
+libc = { version = "0.2.140", default-features = false, features = ['rustc-dep-of-std'] }
+compiler_builtins = { version = "0.1.91" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
diff --git a/library/std/build.rs b/library/std/build.rs
index ea8796675..cf708db6f 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -6,6 +6,9 @@ fn main() {
if target.contains("freebsd") {
if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() {
println!("cargo:rustc-cfg=freebsd12");
+ } else if env::var("RUST_STD_FREEBSD_13_ABI").is_ok() {
+ println!("cargo:rustc-cfg=freebsd12");
+ println!("cargo:rustc-cfg=freebsd13");
}
} else if target.contains("linux")
|| target.contains("netbsd")
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 742c4cc7c..3afc8287e 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -1446,7 +1446,6 @@ impl<'a, K, V> IterMut<'a, K, V> {
/// (provided by the [`IntoIterator`] trait). See its documentation for more.
///
/// [`into_iter`]: IntoIterator::into_iter
-/// [`IntoIterator`]: crate::iter::IntoIterator
///
/// # Example
///
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index b59f89d32..ac906e682 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -12,13 +12,6 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub};
use super::map::{map_try_reserve_error, RandomState};
-// Future Optimization (FIXME!)
-// ============================
-//
-// Iteration over zero sized values is a noop. There is no need
-// for `bucket.val` in the case of HashSet. I suppose we would need HKT
-// to get rid of it properly.
-
/// A [hash set] implemented as a `HashMap` where the value is `()`.
///
/// As with the [`HashMap`] type, a `HashSet` requires that the elements
@@ -1279,7 +1272,6 @@ pub struct Iter<'a, K: 'a> {
/// (provided by the [`IntoIterator`] trait). See its documentation for more.
///
/// [`into_iter`]: IntoIterator::into_iter
-/// [`IntoIterator`]: crate::iter::IntoIterator
///
/// # Examples
///
diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs
index ae2baba09..42f738acb 100644
--- a/library/std/src/collections/mod.rs
+++ b/library/std/src/collections/mod.rs
@@ -172,7 +172,8 @@
//!
//! ## Iterators
//!
-//! Iterators are a powerful and robust mechanism used throughout Rust's
+//! [Iterators][crate::iter]
+//! are a powerful and robust mechanism used throughout Rust's
//! standard libraries. Iterators provide a sequence of values in a generic,
//! safe, efficient and convenient way. The contents of an iterator are usually
//! *lazily* evaluated, so that only the values that are actually needed are
@@ -252,7 +253,9 @@
//!
//! Several other collection methods also return iterators to yield a sequence
//! of results but avoid allocating an entire collection to store the result in.
-//! This provides maximum flexibility as `collect` or `extend` can be called to
+//! This provides maximum flexibility as
+//! [`collect`][crate::iter::Iterator::collect] or
+//! [`extend`][crate::iter::Extend::extend] can be called to
//! "pipe" the sequence into any collection if desired. Otherwise, the sequence
//! can be looped over with a `for` loop. The iterator can also be discarded
//! after partial use, preventing the computation of the unused items.
@@ -395,8 +398,6 @@
//! // ...but the key hasn't changed. b is still "baz", not "xyz".
//! assert_eq!(map.keys().next().unwrap().b, "baz");
//! ```
-//!
-//! [IntoIterator]: crate::iter::IntoIterator "iter::IntoIterator"
#![stable(feature = "rust1", since = "1.0.0")]
@@ -416,8 +417,10 @@ pub use alloc_crate::collections::{BTreeMap, BTreeSet, BinaryHeap};
pub use alloc_crate::collections::{LinkedList, VecDeque};
#[stable(feature = "rust1", since = "1.0.0")]
+#[doc(inline)]
pub use self::hash_map::HashMap;
#[stable(feature = "rust1", since = "1.0.0")]
+#[doc(inline)]
pub use self::hash_set::HashSet;
#[stable(feature = "try_reserve", since = "1.57.0")]
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 183f9ab3b..d372fa640 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -236,21 +236,14 @@ fn _var(key: &OsStr) -> Result<String, VarError> {
}
/// Fetches the environment variable `key` from the current process, returning
-/// [`None`] if the variable isn't set or there's another error.
+/// [`None`] if the variable isn't set or if there is another error.
///
-/// Note that the method will not check if the environment variable
-/// is valid Unicode. If you want to have an error on invalid UTF-8,
-/// use the [`var`] function instead.
-///
-/// # Errors
-///
-/// This function returns an error if the environment variable isn't set.
-///
-/// This function may return an error if the environment variable's name contains
+/// It may return `None` if the environment variable's name contains
/// the equal sign character (`=`) or the NUL character.
///
-/// This function may return an error if the environment variable's value contains
-/// the NUL character.
+/// Note that this function will not check if the environment variable
+/// is valid Unicode. If you want to have an error on invalid UTF-8,
+/// use the [`var`] function instead.
///
/// # Examples
///
@@ -895,6 +888,7 @@ pub mod consts {
/// - x86_64
/// - arm
/// - aarch64
+ /// - loongarch64
/// - m68k
/// - mips
/// - mips64
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index 6b1f0cba8..408244b2c 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -78,10 +78,14 @@ impl f32 {
/// let f = 3.3_f32;
/// let g = -3.3_f32;
/// let h = -3.7_f32;
+ /// let i = 3.5_f32;
+ /// let j = 4.5_f32;
///
/// assert_eq!(f.round(), 3.0);
/// assert_eq!(g.round(), -3.0);
/// assert_eq!(h.round(), -4.0);
+ /// assert_eq!(i.round(), 4.0);
+ /// assert_eq!(j.round(), 5.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f32 {
unsafe { intrinsics::roundf32(self) }
}
+ /// Returns the nearest integer to a number. Rounds half-way cases to the number
+ /// with an even least significant digit.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(round_ties_even)]
+ ///
+ /// let f = 3.3_f32;
+ /// let g = -3.3_f32;
+ /// let h = 3.5_f32;
+ /// let i = 4.5_f32;
+ ///
+ /// assert_eq!(f.round_ties_even(), 3.0);
+ /// assert_eq!(g.round_ties_even(), -3.0);
+ /// assert_eq!(h.round_ties_even(), 4.0);
+ /// assert_eq!(i.round_ties_even(), 4.0);
+ /// ```
+ #[rustc_allow_incoherent_impl]
+ #[must_use = "method returns a new number and does not mutate the original value"]
+ #[unstable(feature = "round_ties_even", issue = "96710")]
+ #[inline]
+ pub fn round_ties_even(self) -> f32 {
+ unsafe { intrinsics::rintf32(self) }
+ }
+
/// Returns the integer part of `self`.
/// This means that non-integer numbers are always truncated towards zero.
///
@@ -551,8 +581,10 @@ impl f32 {
unsafe { cmath::cbrtf(self) }
}
- /// Calculates the length of the hypotenuse of a right-angle triangle given
- /// legs of length `x` and `y`.
+ /// Compute the distance between the origin and a point (`x`, `y`) on the
+ /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a
+ /// right-angle triangle with other sides having length `x.abs()` and
+ /// `y.abs()`.
///
/// # Examples
///
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
index 6ee295de6..e949def00 100644
--- a/library/std/src/f32/tests.rs
+++ b/library/std/src/f32/tests.rs
@@ -209,6 +209,7 @@ fn test_ceil() {
#[test]
fn test_round() {
+ assert_approx_eq!(2.5f32.round(), 3.0f32);
assert_approx_eq!(1.0f32.round(), 1.0f32);
assert_approx_eq!(1.3f32.round(), 1.0f32);
assert_approx_eq!(1.5f32.round(), 2.0f32);
@@ -222,6 +223,21 @@ fn test_round() {
}
#[test]
+fn test_round_ties_even() {
+ assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32);
+ assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32);
+ assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32);
+ assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32);
+ assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32);
+ assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32);
+ assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32);
+ assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32);
+ assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32);
+ assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32);
+ assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32);
+}
+
+#[test]
fn test_trunc() {
assert_approx_eq!(1.0f32.trunc(), 1.0f32);
assert_approx_eq!(1.3f32.trunc(), 1.0f32);
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index 16359766b..6782b861f 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -78,10 +78,14 @@ impl f64 {
/// let f = 3.3_f64;
/// let g = -3.3_f64;
/// let h = -3.7_f64;
+ /// let i = 3.5_f64;
+ /// let j = 4.5_f64;
///
/// assert_eq!(f.round(), 3.0);
/// assert_eq!(g.round(), -3.0);
/// assert_eq!(h.round(), -4.0);
+ /// assert_eq!(i.round(), 4.0);
+ /// assert_eq!(j.round(), 5.0);
/// ```
#[rustc_allow_incoherent_impl]
#[must_use = "method returns a new number and does not mutate the original value"]
@@ -91,6 +95,32 @@ impl f64 {
unsafe { intrinsics::roundf64(self) }
}
+ /// Returns the nearest integer to a number. Rounds half-way cases to the number
+ /// with an even least significant digit.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(round_ties_even)]
+ ///
+ /// let f = 3.3_f64;
+ /// let g = -3.3_f64;
+ /// let h = 3.5_f64;
+ /// let i = 4.5_f64;
+ ///
+ /// assert_eq!(f.round_ties_even(), 3.0);
+ /// assert_eq!(g.round_ties_even(), -3.0);
+ /// assert_eq!(h.round_ties_even(), 4.0);
+ /// assert_eq!(i.round_ties_even(), 4.0);
+ /// ```
+ #[rustc_allow_incoherent_impl]
+ #[must_use = "method returns a new number and does not mutate the original value"]
+ #[unstable(feature = "round_ties_even", issue = "96710")]
+ #[inline]
+ pub fn round_ties_even(self) -> f64 {
+ unsafe { intrinsics::rintf64(self) }
+ }
+
/// Returns the integer part of `self`.
/// This means that non-integer numbers are always truncated towards zero.
///
@@ -553,8 +583,10 @@ impl f64 {
unsafe { cmath::cbrt(self) }
}
- /// Calculates the length of the hypotenuse of a right-angle triangle given
- /// legs of length `x` and `y`.
+ /// Compute the distance between the origin and a point (`x`, `y`) on the
+ /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a
+ /// right-angle triangle with other sides having length `x.abs()` and
+ /// `y.abs()`.
///
/// # Examples
///
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
index 5b039d445..53d351cce 100644
--- a/library/std/src/f64/tests.rs
+++ b/library/std/src/f64/tests.rs
@@ -199,6 +199,7 @@ fn test_ceil() {
#[test]
fn test_round() {
+ assert_approx_eq!(2.5f64.round(), 3.0f64);
assert_approx_eq!(1.0f64.round(), 1.0f64);
assert_approx_eq!(1.3f64.round(), 1.0f64);
assert_approx_eq!(1.5f64.round(), 2.0f64);
@@ -212,6 +213,21 @@ fn test_round() {
}
#[test]
+fn test_round_ties_even() {
+ assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64);
+ assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64);
+ assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64);
+ assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64);
+ assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64);
+ assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64);
+ assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64);
+ assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64);
+ assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64);
+ assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64);
+ assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64);
+}
+
+#[test]
fn test_trunc() {
assert_approx_eq!(1.0f64.trunc(), 1.0f64);
assert_approx_eq!(1.3f64.trunc(), 1.0f64);
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 80ed34157..5c0541d3c 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -6,7 +6,6 @@ use crate::cmp;
use crate::collections::TryReserveError;
use crate::fmt;
use crate::hash::{Hash, Hasher};
-use crate::iter::Extend;
use crate::ops;
use crate::rc::Rc;
use crate::str::FromStr;
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 909d9bf40..401def184 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -2,7 +2,8 @@ use crate::io::prelude::*;
use crate::env;
use crate::fs::{self, File, OpenOptions};
-use crate::io::{ErrorKind, SeekFrom};
+use crate::io::{BorrowedBuf, ErrorKind, SeekFrom};
+use crate::mem::MaybeUninit;
use crate::path::Path;
use crate::str;
use crate::sync::Arc;
@@ -402,6 +403,23 @@ fn file_test_io_seek_read_write() {
}
#[test]
+fn file_test_read_buf() {
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("test");
+ check!(fs::write(filename, &[1, 2, 3, 4]));
+
+ let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
+ let mut buf = BorrowedBuf::from(buf.as_mut_slice());
+ let mut file = check!(File::open(filename));
+ check!(file.read_buf(buf.unfilled()));
+ assert_eq!(buf.filled(), &[1, 2, 3, 4]);
+ // File::read_buf should omit buffer initialization.
+ assert_eq!(buf.init_len(), 4);
+
+ check!(fs::remove_file(filename));
+}
+
+#[test]
fn file_test_stat_is_correct_on_is_file() {
let tmpdir = tmpdir();
let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs
index 6acb937e7..14c455d4f 100644
--- a/library/std/src/io/buffered/bufwriter.rs
+++ b/library/std/src/io/buffered/bufwriter.rs
@@ -339,7 +339,7 @@ impl<W: Write> BufWriter<W> {
let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
// SAFETY: forget(self) prevents double dropping inner
- let inner = unsafe { ptr::read(&mut self.inner) };
+ let inner = unsafe { ptr::read(&self.inner) };
mem::forget(self);
(inner, buf)
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index d98ab021c..25c64240e 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -34,7 +34,7 @@ use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
/// use std::fs::File;
///
/// // a library function we've written
-/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
+/// fn write_ten_bytes_at_end<W: Write + Seek>(mut writer: W) -> io::Result<()> {
/// writer.seek(SeekFrom::End(-10))?;
///
/// for i in 0..10 {
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index 7f07e4fdd..34c0ce9dc 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -11,7 +11,6 @@ mod repr_unpacked;
#[cfg(not(target_pointer_width = "64"))]
use repr_unpacked::Repr;
-use crate::convert::From;
use crate::error;
use crate::fmt;
use crate::result;
@@ -370,7 +369,7 @@ pub enum ErrorKind {
// "Unusual" error kinds which do not correspond simply to (sets
// of) OS error codes, should be added just above this comment.
- // `Other` and `Uncategorised` should remain at the end:
+ // `Other` and `Uncategorized` should remain at the end:
//
/// A custom error that does not fall under any other I/O error kind.
///
@@ -882,6 +881,13 @@ impl Error {
/// Returns the corresponding [`ErrorKind`] for this error.
///
+ /// This may be a value set by Rust code constructing custom `io::Error`s,
+ /// or if this `io::Error` was sourced from the operating system,
+ /// it will be a value inferred from the system's error encoding.
+ /// See [`last_os_error`] for more details.
+ ///
+ /// [`last_os_error`]: Error::last_os_error
+ ///
/// # Examples
///
/// ```
@@ -892,7 +898,8 @@ impl Error {
/// }
///
/// fn main() {
- /// // Will print "Uncategorized".
+ /// // As no error has (visibly) occurred, this may print anything!
+ /// // It likely prints a placeholder for unidentified (non-)errors.
/// print_error(Error::last_os_error());
/// // Will print "AddrInUse".
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index b2b6d8613..ea66d0409 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -268,7 +268,7 @@ pub(crate) use self::stdio::attempt_print_to_stderr;
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::set_output_capture;
-#[unstable(feature = "is_terminal", issue = "98070")]
+#[stable(feature = "is_terminal", since = "1.70.0")]
pub use self::stdio::IsTerminal;
#[unstable(feature = "print_internals", issue = "none")]
pub use self::stdio::{_eprint, _print};
@@ -823,8 +823,22 @@ pub trait Read {
/// Read the exact number of bytes required to fill `cursor`.
///
- /// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`BorrowedCursor`] rather than `[u8]` to
- /// allow use with uninitialized buffers.
+ /// This is similar to the [`read_exact`](Read::read_exact) method, except
+ /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use
+ /// with uninitialized buffers.
+ ///
+ /// # Errors
+ ///
+ /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
+ /// then the error is ignored and the operation will continue.
+ ///
+ /// If this function encounters an "end of file" before completely filling
+ /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
+ ///
+ /// If any other read error is encountered then this function immediately
+ /// returns.
+ ///
+ /// If this function returns an error, all bytes read will be appended to `cursor`.
#[unstable(feature = "read_buf", issue = "78485")]
fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> {
while cursor.capacity() > 0 {
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 14bfef4c7..9098d36ee 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -8,7 +8,7 @@ use crate::io::prelude::*;
use crate::cell::{Cell, RefCell};
use crate::fmt;
use crate::fs::File;
-use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
+use crate::io::{self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard};
use crate::sys::stdio;
@@ -97,6 +97,10 @@ impl Read for StdinRaw {
handle_ebadf(self.0.read(buf), 0)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ handle_ebadf(self.0.read_buf(buf), ())
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
handle_ebadf(self.0.read_vectored(bufs), 0)
}
@@ -418,6 +422,9 @@ impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.lock().read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.lock().read_buf(buf)
+ }
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.lock().read_vectored(bufs)
}
@@ -450,6 +457,10 @@ impl Read for StdinLock<'_> {
self.inner.read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
@@ -1036,13 +1047,23 @@ pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) {
}
/// Trait to determine if a descriptor/handle refers to a terminal/tty.
-#[unstable(feature = "is_terminal", issue = "98070")]
+#[stable(feature = "is_terminal", since = "1.70.0")]
pub trait IsTerminal: crate::sealed::Sealed {
/// Returns `true` if the descriptor/handle refers to a terminal/tty.
///
/// On platforms where Rust does not know how to detect a terminal yet, this will return
/// `false`. This will also return `false` if an unexpected error occurred, such as from
/// passing an invalid file descriptor.
+ ///
+ /// # Platform-specific behavior
+ ///
+ /// On Windows, in addition to detecting consoles, this currently uses some heuristics to
+ /// detect older msys/cygwin/mingw pseudo-terminals based on device name: devices with names
+ /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals.
+ /// Note that this [may change in the future][changes].
+ ///
+ /// [changes]: io#platform-specific-behavior
+ #[stable(feature = "is_terminal", since = "1.70.0")]
fn is_terminal(&self) -> bool;
}
@@ -1051,7 +1072,7 @@ macro_rules! impl_is_terminal {
#[unstable(feature = "sealed", issue = "none")]
impl crate::sealed::Sealed for $t {}
- #[unstable(feature = "is_terminal", issue = "98070")]
+ #[stable(feature = "is_terminal", since = "1.70.0")]
impl IsTerminal for $t {
#[inline]
fn is_terminal(&self) -> bool {
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index 203c490fa..43842bee9 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -1678,7 +1678,7 @@ mod super_keyword {}
/// below `Iterator` is a **supertrait** and `ThreeIterator` is a **subtrait**:
///
/// ```rust
-/// trait ThreeIterator: std::iter::Iterator {
+/// trait ThreeIterator: Iterator {
/// fn next_three(&mut self) -> Option<[Self::Item; 3]>;
/// }
/// ```
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index b62f3ad29..98fcc76aa 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -235,6 +235,7 @@
#![cfg_attr(windows, feature(round_char_boundary))]
//
// Language features:
+// tidy-alphabetical-start
#![feature(alloc_error_handler)]
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
@@ -254,11 +255,10 @@
#![feature(exhaustive_patterns)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
-#![feature(is_terminal)]
#![feature(lang_items)]
#![feature(let_chains)]
-#![feature(linkage)]
#![feature(link_cfg)]
+#![feature(linkage)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
#![feature(needs_panic_runtime)]
@@ -272,9 +272,10 @@
#![feature(thread_local)]
#![feature(try_blocks)]
#![feature(utf8_chunks)]
+// tidy-alphabetical-end
//
// Library features (core):
-#![feature(atomic_mut_ptr)]
+// tidy-alphabetical-start
#![feature(char_internals)]
#![feature(core_intrinsics)]
#![feature(duration_constants)]
@@ -290,10 +291,9 @@
#![feature(hashmap_internals)]
#![feature(ip)]
#![feature(ip_in_core)]
-#![feature(is_some_and)]
#![feature(maybe_uninit_slice)]
+#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_write_slice)]
-#![feature(nonnull_slice_from_raw_parts)]
#![feature(panic_can_unwind)]
#![feature(panic_info_message)]
#![feature(panic_internals)]
@@ -304,30 +304,34 @@
#![feature(provide_any)]
#![feature(ptr_as_uninit)]
#![feature(raw_os_nonzero)]
+#![feature(round_ties_even)]
#![feature(slice_internals)]
#![feature(slice_ptr_get)]
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(strict_provenance)]
-#![feature(maybe_uninit_uninit_array)]
-#![feature(const_maybe_uninit_uninit_array)]
-#![feature(const_waker)]
+// tidy-alphabetical-end
//
// Library features (alloc):
+// tidy-alphabetical-start
#![feature(alloc_layout_extra)]
#![feature(allocator_api)]
#![feature(get_mut_unchecked)]
#![feature(map_try_insert)]
#![feature(new_uninit)]
+#![feature(slice_concat_trait)]
#![feature(thin_box)]
#![feature(try_reserve_kind)]
#![feature(vec_into_raw_parts)]
-#![feature(slice_concat_trait)]
+// tidy-alphabetical-end
//
// Library features (unwind):
+// tidy-alphabetical-start
#![feature(panic_unwind)]
+// tidy-alphabetical-end
//
// Only for re-exporting:
+// tidy-alphabetical-start
#![feature(assert_matches)]
#![feature(async_iterator)]
#![feature(c_variadic)]
@@ -339,24 +343,29 @@
#![feature(custom_test_frameworks)]
#![feature(edition_panic)]
#![feature(format_args_nl)]
+#![feature(get_many_mut)]
+#![feature(lazy_cell)]
#![feature(log_syntax)]
-#![feature(once_cell)]
#![feature(saturating_int_impl)]
#![feature(stdsimd)]
#![feature(test)]
#![feature(trace_macros)]
-#![feature(get_many_mut)]
+// tidy-alphabetical-end
//
// Only used in tests/benchmarks:
//
// Only for const-ness:
+// tidy-alphabetical-start
#![feature(const_collections_with_hasher)]
#![feature(const_hash)]
#![feature(const_io_structs)]
#![feature(const_ip)]
#![feature(const_ipv4)]
#![feature(const_ipv6)]
+#![feature(const_maybe_uninit_uninit_array)]
+#![feature(const_waker)]
#![feature(thread_local_internals)]
+// tidy-alphabetical-end
//
#![default_lib_allocator]
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index ac09a8059..4b42ad65e 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -6,7 +6,7 @@ mod tests;
use crate::io::prelude::*;
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::iter::FusedIterator;
use crate::net::{Shutdown, SocketAddr, ToSocketAddrs};
use crate::sys_common::net as net_imp;
@@ -619,6 +619,10 @@ impl Read for TcpStream {
self.0.read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0.read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
@@ -653,6 +657,10 @@ impl Read for &TcpStream {
self.0.read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0.read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
@@ -861,7 +869,7 @@ impl TcpListener {
/// use std::net::{TcpListener, TcpStream};
///
/// fn listen_on(port: u16) -> impl Iterator<Item = TcpStream> {
- /// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
+ /// let listener = TcpListener::bind(("127.0.0.1", port)).unwrap();
/// listener.into_incoming()
/// .filter_map(Result::ok) /* Ignore failed connections */
/// }
diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs
index e019bc0b6..7a3c66e45 100644
--- a/library/std/src/net/tcp/tests.rs
+++ b/library/std/src/net/tcp/tests.rs
@@ -1,6 +1,7 @@
use crate::fmt;
use crate::io::prelude::*;
-use crate::io::{ErrorKind, IoSlice, IoSliceMut};
+use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut};
+use crate::mem::MaybeUninit;
use crate::net::test::{next_test_ip4, next_test_ip6};
use crate::net::*;
use crate::sync::mpsc::channel;
@@ -280,6 +281,31 @@ fn partial_read() {
}
#[test]
+fn read_buf() {
+ each_ip(&mut |addr| {
+ let srv = t!(TcpListener::bind(&addr));
+ let t = thread::spawn(move || {
+ let mut s = t!(TcpStream::connect(&addr));
+ s.write_all(&[1, 2, 3, 4]).unwrap();
+ });
+
+ let mut s = t!(srv.accept()).0;
+ let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
+ let mut buf = BorrowedBuf::from(buf.as_mut_slice());
+ t!(s.read_buf(buf.unfilled()));
+ assert_eq!(buf.filled(), &[1, 2, 3, 4]);
+
+ // FIXME: sgx uses default_read_buf that initializes the buffer.
+ if cfg!(not(target_env = "sgx")) {
+ // TcpStream::read_buf should omit buffer initialization.
+ assert_eq!(buf.init_len(), 4);
+ }
+
+ t.join().ok().expect("thread panicked");
+ })
+}
+
+#[test]
fn read_vectored() {
each_ip(&mut |addr| {
let srv = t!(TcpListener::bind(&addr));
diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs
index 7cecd1bbf..fe40d6319 100644
--- a/library/std/src/os/android/net.rs
+++ b/library/std/src/os/android/net.rs
@@ -1,8 +1,8 @@
//! Android-specific networking functionality.
-#![unstable(feature = "tcp_quickack", issue = "96256")]
+#![stable(feature = "unix_socket_abstract", since = "1.70.0")]
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub use crate::os::net::linux_ext::addr::SocketAddrExt;
#[unstable(feature = "tcp_quickack", issue = "96256")]
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 99a4e0b51..2180d2974 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -201,7 +201,7 @@ macro_rules! impl_is_terminal {
#[unstable(feature = "sealed", issue = "none")]
impl crate::sealed::Sealed for $t {}
- #[unstable(feature = "is_terminal", issue = "98070")]
+ #[stable(feature = "is_terminal", since = "1.70.0")]
impl crate::io::IsTerminal for $t {
#[inline]
fn is_terminal(&self) -> bool {
@@ -268,7 +268,7 @@ impl AsFd for OwnedFd {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
// Safety: `OwnedFd` and `BorrowedFd` have the same validity
- // invariants, and the `BorrowdFd` is bounded by the lifetime
+ // invariants, and the `BorrowedFd` is bounded by the lifetime
// of `&self`.
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
}
diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs
index 94081c8dd..c8e734d74 100644
--- a/library/std/src/os/linux/net.rs
+++ b/library/std/src/os/linux/net.rs
@@ -1,8 +1,8 @@
//! Linux-specific networking functionality.
-#![unstable(feature = "tcp_quickack", issue = "96256")]
+#![stable(feature = "unix_socket_abstract", since = "1.70.0")]
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub use crate::os::net::linux_ext::addr::SocketAddrExt;
#[unstable(feature = "tcp_quickack", issue = "96256")]
diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs
index f46028c3a..c55ca8ba2 100644
--- a/library/std/src/os/linux/raw.rs
+++ b/library/std/src/os/linux/raw.rs
@@ -231,6 +231,7 @@ mod arch {
}
#[cfg(any(
+ target_arch = "loongarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64",
diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs
index 85065984f..aed772056 100644
--- a/library/std/src/os/net/linux_ext/addr.rs
+++ b/library/std/src/os/net/linux_ext/addr.rs
@@ -4,7 +4,7 @@ use crate::os::unix::net::SocketAddr;
use crate::sealed::Sealed;
/// Platform-specific extensions to [`SocketAddr`].
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub trait SocketAddrExt: Sealed {
/// Creates a Unix socket address in the abstract namespace.
///
@@ -22,7 +22,6 @@ pub trait SocketAddrExt: Sealed {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, SocketAddr};
/// use std::os::linux::net::SocketAddrExt;
///
@@ -38,6 +37,7 @@ pub trait SocketAddrExt: Sealed {
/// Ok(())
/// }
/// ```
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
fn from_abstract_name<N>(name: N) -> crate::io::Result<SocketAddr>
where
N: AsRef<[u8]>;
@@ -47,7 +47,6 @@ pub trait SocketAddrExt: Sealed {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, SocketAddr};
/// use std::os::linux::net::SocketAddrExt;
///
@@ -60,5 +59,6 @@ pub trait SocketAddrExt: Sealed {
/// Ok(())
/// }
/// ```
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
fn as_abstract_name(&self) -> Option<&[u8]>;
}
diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs
index 318ebacfd..62e78cc50 100644
--- a/library/std/src/os/net/linux_ext/mod.rs
+++ b/library/std/src/os/net/linux_ext/mod.rs
@@ -2,7 +2,7 @@
#![doc(cfg(any(target_os = "linux", target_os = "android")))]
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub(crate) mod addr;
#[unstable(feature = "tcp_quickack", issue = "96256")]
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index ece2b33bd..6c99e8c36 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -245,12 +245,12 @@ impl SocketAddr {
}
}
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
impl Sealed for SocketAddr {}
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
#[cfg(any(doc, target_os = "android", target_os = "linux"))]
-#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+#[stable(feature = "unix_socket_abstract", since = "1.70.0")]
impl linux_ext::addr::SocketAddrExt for SocketAddr {
fn as_abstract_name(&self) -> Option<&[u8]> {
if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs
index 7cc901a79..7565fbc0d 100644
--- a/library/std/src/os/unix/net/ancillary.rs
+++ b/library/std/src/os/unix/net/ancillary.rs
@@ -86,7 +86,12 @@ fn add_to_ancillary_data<T>(
cmsg_level: libc::c_int,
cmsg_type: libc::c_int,
) -> bool {
- let source_len = if let Some(source_len) = source.len().checked_mul(size_of::<T>()) {
+ #[cfg(not(target_os = "freebsd"))]
+ let cmsg_size = source.len().checked_mul(size_of::<T>());
+ #[cfg(target_os = "freebsd")]
+ let cmsg_size = Some(unsafe { libc::SOCKCRED2SIZE(1) });
+
+ let source_len = if let Some(source_len) = cmsg_size {
if let Ok(source_len) = u32::try_from(source_len) {
source_len
} else {
@@ -178,7 +183,13 @@ impl<'a, T> Iterator for AncillaryDataIter<'a, T> {
}
}
-#[cfg(all(doc, not(target_os = "android"), not(target_os = "linux"), not(target_os = "netbsd")))]
+#[cfg(all(
+ doc,
+ not(target_os = "android"),
+ not(target_os = "linux"),
+ not(target_os = "netbsd"),
+ not(target_os = "freebsd")
+))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
#[derive(Clone)]
pub struct SocketCred(());
@@ -194,6 +205,11 @@ pub struct SocketCred(libc::ucred);
#[derive(Clone)]
pub struct SocketCred(libc::sockcred);
+#[cfg(target_os = "freebsd")]
+#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+#[derive(Clone)]
+pub struct SocketCred(libc::sockcred2);
+
#[doc(cfg(any(target_os = "android", target_os = "linux")))]
#[cfg(any(target_os = "android", target_os = "linux"))]
impl SocketCred {
@@ -246,6 +262,66 @@ impl SocketCred {
}
}
+#[cfg(target_os = "freebsd")]
+impl SocketCred {
+ /// Create a Unix credential struct.
+ ///
+ /// PID, UID and GID is set to 0.
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ #[must_use]
+ pub fn new() -> SocketCred {
+ SocketCred(libc::sockcred2 {
+ sc_version: 0,
+ sc_pid: 0,
+ sc_uid: 0,
+ sc_euid: 0,
+ sc_gid: 0,
+ sc_egid: 0,
+ sc_ngroups: 0,
+ sc_groups: [0; 1],
+ })
+ }
+
+ /// Set the PID.
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn set_pid(&mut self, pid: libc::pid_t) {
+ self.0.sc_pid = pid;
+ }
+
+ /// Get the current PID.
+ #[must_use]
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn get_pid(&self) -> libc::pid_t {
+ self.0.sc_pid
+ }
+
+ /// Set the UID.
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn set_uid(&mut self, uid: libc::uid_t) {
+ self.0.sc_euid = uid;
+ }
+
+ /// Get the current UID.
+ #[must_use]
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn get_uid(&self) -> libc::uid_t {
+ self.0.sc_euid
+ }
+
+ /// Set the GID.
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn set_gid(&mut self, gid: libc::gid_t) {
+ self.0.sc_egid = gid;
+ }
+
+ /// Get the current GID.
+ #[must_use]
+ #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+ pub fn get_gid(&self) -> libc::gid_t {
+ self.0.sc_egid
+ }
+}
+
#[cfg(target_os = "netbsd")]
impl SocketCred {
/// Create a Unix credential struct.
@@ -271,6 +347,7 @@ impl SocketCred {
}
/// Get the current PID.
+ #[must_use]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn get_pid(&self) -> libc::pid_t {
self.0.sc_pid
@@ -283,6 +360,7 @@ impl SocketCred {
}
/// Get the current UID.
+ #[must_use]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn get_uid(&self) -> libc::uid_t {
self.0.sc_uid
@@ -295,6 +373,7 @@ impl SocketCred {
}
/// Get the current GID.
+ #[must_use]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn get_gid(&self) -> libc::gid_t {
self.0.sc_gid
@@ -316,7 +395,13 @@ impl<'a> Iterator for ScmRights<'a> {
}
}
-#[cfg(all(doc, not(target_os = "android"), not(target_os = "linux"), not(target_os = "netbsd")))]
+#[cfg(all(
+ doc,
+ not(target_os = "android"),
+ not(target_os = "linux"),
+ not(target_os = "netbsd"),
+ not(target_os = "freebsd")
+))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub struct ScmCredentials<'a>(AncillaryDataIter<'a, ()>);
@@ -327,11 +412,21 @@ pub struct ScmCredentials<'a>(AncillaryDataIter<'a, ()>);
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::ucred>);
+#[cfg(target_os = "freebsd")]
+#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::sockcred2>);
+
#[cfg(target_os = "netbsd")]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::sockcred>);
-#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+#[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
impl<'a> Iterator for ScmCredentials<'a> {
type Item = SocketCred;
@@ -353,7 +448,13 @@ pub enum AncillaryError {
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub enum AncillaryData<'a> {
ScmRights(ScmRights<'a>),
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
ScmCredentials(ScmCredentials<'a>),
}
@@ -376,7 +477,13 @@ impl<'a> AncillaryData<'a> {
///
/// `data` must contain a valid control message and the control message must be type of
/// `SOL_SOCKET` and level of `SCM_CREDENTIALS` or `SCM_CREDS`.
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
unsafe fn as_credentials(data: &'a [u8]) -> Self {
let ancillary_data_iter = AncillaryDataIter::new(data);
let scm_credentials = ScmCredentials(ancillary_data_iter);
@@ -395,6 +502,8 @@ impl<'a> AncillaryData<'a> {
libc::SCM_RIGHTS => Ok(AncillaryData::as_rights(data)),
#[cfg(any(target_os = "android", target_os = "linux",))]
libc::SCM_CREDENTIALS => Ok(AncillaryData::as_credentials(data)),
+ #[cfg(target_os = "freebsd")]
+ libc::SCM_CREDS2 => Ok(AncillaryData::as_credentials(data)),
#[cfg(target_os = "netbsd")]
libc::SCM_CREDS => Ok(AncillaryData::as_credentials(data)),
cmsg_type => {
@@ -603,12 +712,18 @@ impl<'a> SocketAncillary<'a> {
/// Add credentials to the ancillary data.
///
- /// The function returns `true` if there was enough space in the buffer.
- /// If there was not enough space then no credentials was appended.
+ /// The function returns `true` if there is enough space in the buffer.
+ /// If there is not enough space then no credentials will be appended.
/// Technically, that means this operation adds a control message with the level `SOL_SOCKET`
- /// and type `SCM_CREDENTIALS` or `SCM_CREDS`.
- ///
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ /// and type `SCM_CREDENTIALS`, `SCM_CREDS`, or `SCM_CREDS2`.
+ ///
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool {
self.truncated = false;
@@ -617,8 +732,10 @@ impl<'a> SocketAncillary<'a> {
&mut self.length,
creds,
libc::SOL_SOCKET,
- #[cfg(not(target_os = "netbsd"))]
+ #[cfg(not(any(target_os = "netbsd", target_os = "freebsd")))]
libc::SCM_CREDENTIALS,
+ #[cfg(target_os = "freebsd")]
+ libc::SCM_CREDS2,
#[cfg(target_os = "netbsd")]
libc::SCM_CREDS,
)
diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index 272b4f5dc..34db54235 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -102,7 +102,6 @@ impl UnixDatagram {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
@@ -119,7 +118,7 @@ impl UnixDatagram {
/// Ok(())
/// }
/// ```
- #[unstable(feature = "unix_socket_abstract", issue = "85410")]
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixDatagram> {
unsafe {
let socket = UnixDatagram::unbound()?;
@@ -217,7 +216,6 @@ impl UnixDatagram {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
@@ -235,7 +233,7 @@ impl UnixDatagram {
/// Ok(())
/// }
/// ```
- #[unstable(feature = "unix_socket_abstract", issue = "85410")]
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> {
unsafe {
cvt(libc::connect(
@@ -523,7 +521,6 @@ impl UnixDatagram {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixDatagram};
///
/// fn main() -> std::io::Result<()> {
@@ -535,7 +532,7 @@ impl UnixDatagram {
/// Ok(())
/// }
/// ```
- #[unstable(feature = "unix_socket_abstract", issue = "85410")]
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result<usize> {
unsafe {
let count = cvt(libc::sendto(
@@ -811,8 +808,24 @@ impl UnixDatagram {
///
/// # Examples
///
- #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")]
- #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")]
+ #[cfg_attr(
+ any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ ),
+ doc = "```no_run"
+ )]
+ #[cfg_attr(
+ not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ )),
+ doc = "```ignore"
+ )]
/// #![feature(unix_socket_ancillary_data)]
/// use std::os::unix::net::UnixDatagram;
///
@@ -822,7 +835,13 @@ impl UnixDatagram {
/// Ok(())
/// }
/// ```
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
self.0.set_passcred(passcred)
@@ -834,7 +853,13 @@ impl UnixDatagram {
/// Get the socket option `SO_PASSCRED`.
///
/// [`set_passcred`]: UnixDatagram::set_passcred
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn passcred(&self) -> io::Result<bool> {
self.0.passcred()
diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs
index 02090afc8..5be8aebc7 100644
--- a/library/std/src/os/unix/net/listener.rs
+++ b/library/std/src/os/unix/net/listener.rs
@@ -90,7 +90,6 @@ impl UnixListener {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener};
///
/// fn main() -> std::io::Result<()> {
@@ -107,7 +106,7 @@ impl UnixListener {
/// Ok(())
/// }
/// ```
- #[unstable(feature = "unix_socket_abstract", issue = "85410")]
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index dff8f6e85..bf2a51b5e 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -106,7 +106,6 @@ impl UnixStream {
/// # Examples
///
/// ```no_run
- /// #![feature(unix_socket_abstract)]
/// use std::os::unix::net::{UnixListener, UnixStream};
///
/// fn main() -> std::io::Result<()> {
@@ -123,7 +122,7 @@ impl UnixStream {
/// Ok(())
/// }
/// ````
- #[unstable(feature = "unix_socket_abstract", issue = "85410")]
+ #[stable(feature = "unix_socket_abstract", since = "1.70.0")]
pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result<UnixStream> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
@@ -398,8 +397,24 @@ impl UnixStream {
///
/// # Examples
///
- #[cfg_attr(any(target_os = "android", target_os = "linux"), doc = "```no_run")]
- #[cfg_attr(not(any(target_os = "android", target_os = "linux")), doc = "```ignore")]
+ #[cfg_attr(
+ any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ),
+ doc = "```no_run"
+ )]
+ #[cfg_attr(
+ not(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ )),
+ doc = "```ignore"
+ )]
/// #![feature(unix_socket_ancillary_data)]
/// use std::os::unix::net::UnixStream;
///
@@ -409,7 +424,13 @@ impl UnixStream {
/// Ok(())
/// }
/// ```
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
self.0.set_passcred(passcred)
@@ -421,7 +442,13 @@ impl UnixStream {
/// Get the socket option `SO_PASSCRED`.
///
/// [`set_passcred`]: UnixStream::set_passcred
- #[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
+ #[cfg(any(
+ doc,
+ target_os = "android",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd"
+ ))]
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub fn passcred(&self) -> io::Result<bool> {
self.0.passcred()
diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs
index f8c29a6d3..39f10c50d 100644
--- a/library/std/src/os/unix/net/tests.rs
+++ b/library/std/src/os/unix/net/tests.rs
@@ -646,7 +646,7 @@ fn test_send_vectored_fds_unix_stream() {
}
}
-#[cfg(any(target_os = "android", target_os = "linux",))]
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
#[test]
fn test_send_vectored_with_ancillary_to_unix_datagram() {
fn getpid() -> libc::pid_t {
diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs
index 1dfecc573..50410fcdf 100644
--- a/library/std/src/os/windows/io/handle.rs
+++ b/library/std/src/os/windows/io/handle.rs
@@ -389,7 +389,7 @@ macro_rules! impl_is_terminal {
#[unstable(feature = "sealed", issue = "none")]
impl crate::sealed::Sealed for $t {}
- #[unstable(feature = "is_terminal", issue = "98070")]
+ #[stable(feature = "is_terminal", since = "1.70.0")]
impl crate::io::IsTerminal for $t {
#[inline]
fn is_terminal(&self) -> bool {
diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs
index 9fa8f5702..345d72ef8 100644
--- a/library/std/src/panic.rs
+++ b/library/std/src/panic.rs
@@ -308,8 +308,7 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> {
BacktraceStyle::Short
}
})
- .unwrap_or(if cfg!(target_os = "fuchsia") {
- // Fuchsia components default to full backtrace.
+ .unwrap_or(if crate::sys::FULL_BACKTRACE_DEFAULT {
BacktraceStyle::Full
} else {
BacktraceStyle::Off
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index e59f32af7..a46a29cba 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -46,12 +46,10 @@ extern "C" {
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
-#[allow(improper_ctypes)]
extern "Rust" {
- /// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not
- /// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations
- /// when using the "abort" panic runtime).
- fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32;
+ /// `BoxMeUp` lazily performs allocation only when needed (this avoids
+ /// allocations when using the "abort" panic runtime).
+ fn __rust_start_panic(payload: &mut dyn BoxMeUp) -> u32;
}
/// This function is called by the panic runtime if FFI code catches a Rust
@@ -500,6 +498,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
// This function cannot be marked as `unsafe` because `intrinsics::r#try`
// expects normal function pointers.
#[inline]
+ #[rustc_nounwind] // `intrinsic::r#try` requires catch fn to be nounwind
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
// SAFETY: this is the responsibility of the caller, see above.
//
@@ -738,10 +737,7 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
/// yer breakpoints.
#[inline(never)]
#[cfg_attr(not(test), rustc_std_internal_symbol)]
-fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
- let code = unsafe {
- let obj = &mut msg as *mut &mut dyn BoxMeUp;
- __rust_start_panic(obj)
- };
+fn rust_panic(msg: &mut dyn BoxMeUp) -> ! {
+ let code = unsafe { __rust_start_panic(msg) };
rtabort!("failed to initiate panic, error {code}")
}
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index cd6b393a2..b3d883de0 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -78,7 +78,7 @@ use crate::fmt;
use crate::fs;
use crate::hash::{Hash, Hasher};
use crate::io;
-use crate::iter::{self, FusedIterator};
+use crate::iter::FusedIterator;
use crate::ops::{self, Deref};
use crate::rc::Rc;
use crate::str::FromStr;
@@ -450,26 +450,26 @@ impl<'a> PrefixComponent<'a> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a> cmp::PartialEq for PrefixComponent<'a> {
+impl<'a> PartialEq for PrefixComponent<'a> {
#[inline]
fn eq(&self, other: &PrefixComponent<'a>) -> bool {
- cmp::PartialEq::eq(&self.parsed, &other.parsed)
+ self.parsed == other.parsed
}
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a> cmp::PartialOrd for PrefixComponent<'a> {
+impl<'a> PartialOrd for PrefixComponent<'a> {
#[inline]
fn partial_cmp(&self, other: &PrefixComponent<'a>) -> Option<cmp::Ordering> {
- cmp::PartialOrd::partial_cmp(&self.parsed, &other.parsed)
+ PartialOrd::partial_cmp(&self.parsed, &other.parsed)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Ord for PrefixComponent<'_> {
+impl Ord for PrefixComponent<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
- cmp::Ord::cmp(&self.parsed, &other.parsed)
+ Ord::cmp(&self.parsed, &other.parsed)
}
}
@@ -988,7 +988,7 @@ impl<'a> DoubleEndedIterator for Components<'a> {
impl FusedIterator for Components<'_> {}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a> cmp::PartialEq for Components<'a> {
+impl<'a> PartialEq for Components<'a> {
#[inline]
fn eq(&self, other: &Components<'a>) -> bool {
let Components { path: _, front: _, back: _, has_physical_root: _, prefix: _ } = self;
@@ -1015,10 +1015,10 @@ impl<'a> cmp::PartialEq for Components<'a> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Eq for Components<'_> {}
+impl Eq for Components<'_> {}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a> cmp::PartialOrd for Components<'a> {
+impl<'a> PartialOrd for Components<'a> {
#[inline]
fn partial_cmp(&self, other: &Components<'a>) -> Option<cmp::Ordering> {
Some(compare_components(self.clone(), other.clone()))
@@ -1026,7 +1026,7 @@ impl<'a> cmp::PartialOrd for Components<'a> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Ord for Components<'_> {
+impl Ord for Components<'_> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
compare_components(self.clone(), other.clone())
@@ -1498,7 +1498,6 @@ impl PathBuf {
/// # Examples
///
/// ```
- /// #![feature(path_as_mut_os_str)]
/// use std::path::{Path, PathBuf};
///
/// let mut path = PathBuf::from("/foo");
@@ -1510,7 +1509,7 @@ impl PathBuf {
/// path.as_mut_os_string().push("baz");
/// assert_eq!(path, Path::new("/foo/barbaz"));
/// ```
- #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+ #[stable(feature = "path_as_mut_os_str", since = "1.70.0")]
#[must_use]
#[inline]
pub fn as_mut_os_string(&mut self) -> &mut OsString {
@@ -1742,7 +1741,7 @@ impl FromStr for PathBuf {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<P: AsRef<Path>> iter::FromIterator<P> for PathBuf {
+impl<P: AsRef<Path>> FromIterator<P> for PathBuf {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> PathBuf {
let mut buf = PathBuf::new();
buf.extend(iter);
@@ -1751,7 +1750,7 @@ impl<P: AsRef<Path>> iter::FromIterator<P> for PathBuf {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<P: AsRef<Path>> iter::Extend<P> for PathBuf {
+impl<P: AsRef<Path>> Extend<P> for PathBuf {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
iter.into_iter().for_each(move |p| self.push(p.as_ref()));
}
@@ -1905,7 +1904,7 @@ impl ToOwned for Path {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::PartialEq for PathBuf {
+impl PartialEq for PathBuf {
#[inline]
fn eq(&self, other: &PathBuf) -> bool {
self.components() == other.components()
@@ -1920,10 +1919,10 @@ impl Hash for PathBuf {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Eq for PathBuf {}
+impl Eq for PathBuf {}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::PartialOrd for PathBuf {
+impl PartialOrd for PathBuf {
#[inline]
fn partial_cmp(&self, other: &PathBuf) -> Option<cmp::Ordering> {
Some(compare_components(self.components(), other.components()))
@@ -1931,7 +1930,7 @@ impl cmp::PartialOrd for PathBuf {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Ord for PathBuf {
+impl Ord for PathBuf {
#[inline]
fn cmp(&self, other: &PathBuf) -> cmp::Ordering {
compare_components(self.components(), other.components())
@@ -2066,7 +2065,6 @@ impl Path {
/// # Examples
///
/// ```
- /// #![feature(path_as_mut_os_str)]
/// use std::path::{Path, PathBuf};
///
/// let mut path = PathBuf::from("Foo.TXT");
@@ -2076,7 +2074,7 @@ impl Path {
/// path.as_mut_os_str().make_ascii_lowercase();
/// assert_eq!(path, Path::new("foo.txt"));
/// ```
- #[unstable(feature = "path_as_mut_os_str", issue = "105021")]
+ #[stable(feature = "path_as_mut_os_str", since = "1.70.0")]
#[must_use]
#[inline]
pub fn as_mut_os_str(&mut self) -> &mut OsStr {
@@ -3027,7 +3025,7 @@ impl fmt::Display for Display<'_> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::PartialEq for Path {
+impl PartialEq for Path {
#[inline]
fn eq(&self, other: &Path) -> bool {
self.components() == other.components()
@@ -3086,10 +3084,10 @@ impl Hash for Path {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Eq for Path {}
+impl Eq for Path {}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::PartialOrd for Path {
+impl PartialOrd for Path {
#[inline]
fn partial_cmp(&self, other: &Path) -> Option<cmp::Ordering> {
Some(compare_components(self.components(), other.components()))
@@ -3097,7 +3095,7 @@ impl cmp::PartialOrd for Path {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl cmp::Ord for Path {
+impl Ord for Path {
#[inline]
fn cmp(&self, other: &Path) -> cmp::Ordering {
compare_components(self.components(), other.components())
diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs
index 41c0fe725..0421b47be 100644
--- a/library/std/src/personality/gcc.rs
+++ b/library/std/src/personality/gcc.rs
@@ -77,6 +77,9 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11
+#[cfg(target_arch = "loongarch64")]
+const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1
+
// The following code is based on GCC's C and C++ personality routines. For reference, see:
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs
index c314bbbb6..1b29c887d 100644
--- a/library/std/src/prelude/mod.rs
+++ b/library/std/src/prelude/mod.rs
@@ -34,7 +34,7 @@
//! marker traits that indicate fundamental properties of types.
//! * <code>[std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}</code>, various
//! operations for both destructors and overloading `()`.
-//! * <code>[std::mem]::[drop][mem::drop]</code>, a convenience function for explicitly
+//! * <code>[std::mem]::[drop]</code>, a convenience function for explicitly
//! dropping a value.
//! * <code>[std::boxed]::[Box]</code>, a way to allocate values on the heap.
//! * <code>[std::borrow]::[ToOwned]</code>, the conversion trait that defines
@@ -66,7 +66,6 @@
//! * <code>[std::convert]::{[TryFrom], [TryInto]}</code>,
//! * <code>[std::iter]::[FromIterator]</code>.
//!
-//! [mem::drop]: crate::mem::drop
//! [std::borrow]: crate::borrow
//! [std::boxed]: crate::boxed
//! [std::clone]: crate::clone
@@ -86,9 +85,6 @@
//! [std::slice]: crate::slice
//! [std::string]: crate::string
//! [std::vec]: mod@crate::vec
-//! [TryFrom]: crate::convert::TryFrom
-//! [TryInto]: crate::convert::TryInto
-//! [FromIterator]: crate::iter::FromIterator
//! [`to_owned`]: crate::borrow::ToOwned::to_owned
//! [book-closures]: ../../book/ch13-01-closures.html
//! [book-dtor]: ../../book/ch15-03-drop.html
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 6f78811a1..3df990e5d 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1,7 +1,8 @@
// `library/{std,core}/src/primitive_docs.rs` should have the same contents.
// These are different files so that relative links work properly without
// having to have `CARGO_PKG_NAME` set, but conceptually they should always be the same.
-#[doc(primitive = "bool")]
+#[cfg_attr(bootstrap, doc(primitive = "bool"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "bool")]
#[doc(alias = "true")]
#[doc(alias = "false")]
/// The boolean type.
@@ -63,7 +64,8 @@
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_bool {}
-#[doc(primitive = "never")]
+#[cfg_attr(bootstrap, doc(primitive = "never"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "never")]
#[doc(alias = "!")]
//
/// The `!` type, also called "never".
@@ -274,7 +276,8 @@ mod prim_bool {}
#[unstable(feature = "never_type", issue = "35121")]
mod prim_never {}
-#[doc(primitive = "char")]
+#[cfg_attr(bootstrap, doc(primitive = "char"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "char")]
#[allow(rustdoc::invalid_rust_codeblocks)]
/// A character type.
///
@@ -398,7 +401,8 @@ mod prim_never {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_char {}
-#[doc(primitive = "unit")]
+#[cfg_attr(bootstrap, doc(primitive = "unit"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "unit")]
#[doc(alias = "(")]
#[doc(alias = ")")]
#[doc(alias = "()")]
@@ -460,7 +464,8 @@ impl Copy for () {
// empty
}
-#[doc(primitive = "pointer")]
+#[cfg_attr(bootstrap, doc(primitive = "pointer"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "pointer")]
#[doc(alias = "ptr")]
#[doc(alias = "*")]
#[doc(alias = "*const")]
@@ -572,12 +577,12 @@ impl Copy for () {
/// [`is_null`]: pointer::is_null
/// [`offset`]: pointer::offset
#[doc = concat!("[`into_raw`]: ", include_str!("../primitive_docs/box_into_raw.md"))]
-/// [`drop`]: mem::drop
/// [`write`]: ptr::write
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_pointer {}
-#[doc(primitive = "array")]
+#[cfg_attr(bootstrap, doc(primitive = "array"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "array")]
#[doc(alias = "[]")]
#[doc(alias = "[T;N]")] // unfortunately, rustdoc doesn't have fuzzy search for aliases
#[doc(alias = "[T; N]")]
@@ -778,7 +783,8 @@ mod prim_pointer {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_array {}
-#[doc(primitive = "slice")]
+#[cfg_attr(bootstrap, doc(primitive = "slice"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "slice")]
#[doc(alias = "[")]
#[doc(alias = "]")]
#[doc(alias = "[]")]
@@ -870,7 +876,8 @@ mod prim_array {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_slice {}
-#[doc(primitive = "str")]
+#[cfg_attr(bootstrap, doc(primitive = "str"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "str")]
/// String slices.
///
/// *[See also the `std::str` module](crate::str).*
@@ -937,7 +944,8 @@ mod prim_slice {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_str {}
-#[doc(primitive = "tuple")]
+#[cfg_attr(bootstrap, doc(primitive = "tuple"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "tuple")]
#[doc(alias = "(")]
#[doc(alias = ")")]
#[doc(alias = "()")]
@@ -1017,7 +1025,6 @@ mod prim_str {}
/// * [`UnwindSafe`]
/// * [`RefUnwindSafe`]
///
-/// [`Unpin`]: marker::Unpin
/// [`UnwindSafe`]: panic::UnwindSafe
/// [`RefUnwindSafe`]: panic::RefUnwindSafe
///
@@ -1081,7 +1088,8 @@ impl<T: Copy> Copy for (T,) {
// empty
}
-#[doc(primitive = "f32")]
+#[cfg_attr(bootstrap, doc(primitive = "f32"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "f32")]
/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008).
///
/// This type can represent a wide range of decimal numbers, like `3.5`, `27`,
@@ -1110,7 +1118,7 @@ impl<T: Copy> Copy for (T,) {
/// - [NaN (not a number)](#associatedconstant.NAN): this value results from
/// calculations like `(-1.0).sqrt()`. NaN has some potentially unexpected
/// behavior:
-/// - It is unequal to any float, including itself! This is the reason `f32`
+/// - It is not equal to any float, including itself! This is the reason `f32`
/// doesn't implement the `Eq` trait.
/// - It is also neither smaller nor greater than any float, making it
/// impossible to sort by the default comparison operation, which is the
@@ -1147,7 +1155,8 @@ impl<T: Copy> Copy for (T,) {
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_f32 {}
-#[doc(primitive = "f64")]
+#[cfg_attr(bootstrap, doc(primitive = "f64"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "f64")]
/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008).
///
/// This type is very similar to [`f32`], but has increased
@@ -1162,67 +1171,78 @@ mod prim_f32 {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_f64 {}
-#[doc(primitive = "i8")]
+#[cfg_attr(bootstrap, doc(primitive = "i8"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "i8")]
//
/// The 8-bit signed integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_i8 {}
-#[doc(primitive = "i16")]
+#[cfg_attr(bootstrap, doc(primitive = "i16"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "i16")]
//
/// The 16-bit signed integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_i16 {}
-#[doc(primitive = "i32")]
+#[cfg_attr(bootstrap, doc(primitive = "i32"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "i32")]
//
/// The 32-bit signed integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_i32 {}
-#[doc(primitive = "i64")]
+#[cfg_attr(bootstrap, doc(primitive = "i64"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "i64")]
//
/// The 64-bit signed integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_i64 {}
-#[doc(primitive = "i128")]
+#[cfg_attr(bootstrap, doc(primitive = "i128"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "i128")]
//
/// The 128-bit signed integer type.
#[stable(feature = "i128", since = "1.26.0")]
mod prim_i128 {}
-#[doc(primitive = "u8")]
+#[cfg_attr(bootstrap, doc(primitive = "u8"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "u8")]
//
/// The 8-bit unsigned integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_u8 {}
-#[doc(primitive = "u16")]
+#[cfg_attr(bootstrap, doc(primitive = "u16"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "u16")]
//
/// The 16-bit unsigned integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_u16 {}
-#[doc(primitive = "u32")]
+#[cfg_attr(bootstrap, doc(primitive = "u32"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "u32")]
//
/// The 32-bit unsigned integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_u32 {}
-#[doc(primitive = "u64")]
+#[cfg_attr(bootstrap, doc(primitive = "u64"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "u64")]
//
/// The 64-bit unsigned integer type.
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_u64 {}
-#[doc(primitive = "u128")]
+#[cfg_attr(bootstrap, doc(primitive = "u128"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "u128")]
//
/// The 128-bit unsigned integer type.
#[stable(feature = "i128", since = "1.26.0")]
mod prim_u128 {}
-#[doc(primitive = "isize")]
+#[cfg_attr(bootstrap, doc(primitive = "isize"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "isize")]
//
/// The pointer-sized signed integer type.
///
@@ -1232,7 +1252,8 @@ mod prim_u128 {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_isize {}
-#[doc(primitive = "usize")]
+#[cfg_attr(bootstrap, doc(primitive = "usize"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "usize")]
//
/// The pointer-sized unsigned integer type.
///
@@ -1242,7 +1263,8 @@ mod prim_isize {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_usize {}
-#[doc(primitive = "reference")]
+#[cfg_attr(bootstrap, doc(primitive = "reference"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "reference")]
#[doc(alias = "&")]
#[doc(alias = "&mut")]
//
@@ -1338,6 +1360,7 @@ mod prim_usize {}
/// * [`Hash`]
/// * [`ToSocketAddrs`]
/// * [`Send`] \(`&T` references also require <code>T: [Sync]</code>)
+/// * [`Sync`]
///
/// [`std::fmt`]: fmt
/// [`Hash`]: hash::Hash
@@ -1373,16 +1396,13 @@ mod prim_usize {}
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_ref {}
-#[doc(primitive = "fn")]
+#[cfg_attr(bootstrap, doc(primitive = "fn"))]
+#[cfg_attr(not(bootstrap), rustc_doc_primitive = "fn")]
//
/// Function pointers, like `fn(usize) -> bool`.
///
/// *See also the traits [`Fn`], [`FnMut`], and [`FnOnce`].*
///
-/// [`Fn`]: ops::Fn
-/// [`FnMut`]: ops::FnMut
-/// [`FnOnce`]: ops::FnOnce
-///
/// Function pointers are pointers that point to *code*, not data. They can be called
/// just like functions. Like references, function pointers are, among other things, assumed to
/// not be null, so if you want to pass a function pointer over FFI and be able to accommodate null
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 1952e19e6..0ab72f7ea 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -110,7 +110,7 @@ use crate::convert::Infallible;
use crate::ffi::OsStr;
use crate::fmt;
use crate::fs;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::num::NonZeroI32;
use crate::path::Path;
use crate::str;
@@ -354,6 +354,10 @@ impl Read for ChildStdout {
self.inner.read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
@@ -419,6 +423,10 @@ impl Read for ChildStderr {
self.inner.read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
@@ -644,10 +652,19 @@ impl Command {
self
}
- /// Inserts or updates an environment variable mapping.
+ /// Inserts or updates an explicit environment variable mapping.
///
- /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
- /// and case-sensitive on all other platforms.
+ /// This method allows you to add an environment variable mapping to the spawned process or
+ /// overwrite a previously set value. You can use [`Command::envs`] to set multiple environment
+ /// variables simultaneously.
+ ///
+ /// Child processes will inherit environment variables from their parent process by default.
+ /// Environment variables explicitly set using [`Command::env`] take precedence over inherited
+ /// variables. You can disable environment variable inheritance entirely using
+ /// [`Command::env_clear`] or for a single key using [`Command::env_remove`].
+ ///
+ /// Note that environment variable names are case-insensitive (but
+ /// case-preserving) on Windows and case-sensitive on all other platforms.
///
/// # Examples
///
@@ -671,7 +688,19 @@ impl Command {
self
}
- /// Adds or updates multiple environment variable mappings.
+ /// Inserts or updates multiple explicit environment variable mappings.
+ ///
+ /// This method allows you to add multiple environment variable mappings to the spawned process
+ /// or overwrite previously set values. You can use [`Command::env`] to set a single environment
+ /// variable.
+ ///
+ /// Child processes will inherit environment variables from their parent process by default.
+ /// Environment variables explicitly set using [`Command::envs`] take precedence over inherited
+ /// variables. You can disable environment variable inheritance entirely using
+ /// [`Command::env_clear`] or for a single key using [`Command::env_remove`].
+ ///
+ /// Note that environment variable names are case-insensitive (but case-preserving) on Windows
+ /// and case-sensitive on all other platforms.
///
/// # Examples
///
@@ -708,7 +737,18 @@ impl Command {
self
}
- /// Removes an environment variable mapping.
+ /// Removes an explicitly set environment variable and prevents inheriting it from a parent
+ /// process.
+ ///
+ /// This method will remove the explicit value of an environment variable set via
+ /// [`Command::env`] or [`Command::envs`]. In addition, it will prevent the spawned child
+ /// process from inheriting that environment variable from its parent process.
+ ///
+ /// After calling [`Command::env_remove`], the value associated with its key from
+ /// [`Command::get_envs`] will be [`None`].
+ ///
+ /// To clear all explicitly set environment variables and disable all environment variable
+ /// inheritance, you can use [`Command::env_clear`].
///
/// # Examples
///
@@ -728,7 +768,17 @@ impl Command {
self
}
- /// Clears the entire environment map for the child process.
+ /// Clears all explicitly set environment variables and prevents inheriting any parent process
+ /// environment variables.
+ ///
+ /// This method will remove all explicitly added environment variables set via [`Command::env`]
+ /// or [`Command::envs`]. In addition, it will prevent the spawned child process from inheriting
+ /// any environment variable from its parent process.
+ ///
+ /// After calling [`Command::env_remove`], the iterator from [`Command::get_envs`] will be
+ /// empty.
+ ///
+ /// You can use [`Command::env_remove`] to clear a single mapping.
///
/// # Examples
///
@@ -980,17 +1030,21 @@ impl Command {
CommandArgs { inner: self.inner.get_args() }
}
- /// Returns an iterator of the environment variables that will be set when
- /// the process is spawned.
+ /// Returns an iterator of the environment variables explicitly set for the child process.
+ ///
+ /// Environment variables explicitly set using [`Command::env`], [`Command::envs`], and
+ /// [`Command::env_remove`] can be retrieved with this method.
+ ///
+ /// Note that this output does not include environment variables inherited from the parent
+ /// process.
///
- /// Each element is a tuple `(&OsStr, Option<&OsStr>)`, where the first
- /// value is the key, and the second is the value, which is [`None`] if
- /// the environment variable is to be explicitly removed.
+ /// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
+ /// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
+ /// the [`None`] value will no longer inherit from its parent process.
///
- /// This only includes environment variables explicitly set with
- /// [`Command::env`], [`Command::envs`], and [`Command::env_remove`]. It
- /// does not include environment variables that will be inherited by the
- /// child process.
+ /// An empty iterator can indicate that no explicit mappings were added or that
+ /// [`Command::env_clear`] was called. After calling [`Command::env_clear`], the child process
+ /// will not inherit any environment variables from its parent process.
///
/// # Examples
///
diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs
index b4f6cc2da..d7f4d335d 100644
--- a/library/std/src/process/tests.rs
+++ b/library/std/src/process/tests.rs
@@ -1,7 +1,8 @@
use crate::io::prelude::*;
use super::{Command, Output, Stdio};
-use crate::io::ErrorKind;
+use crate::io::{BorrowedBuf, ErrorKind};
+use crate::mem::MaybeUninit;
use crate::str;
fn known_command() -> Command {
@@ -121,6 +122,37 @@ fn stdin_works() {
#[test]
#[cfg_attr(any(target_os = "vxworks"), ignore)]
+fn child_stdout_read_buf() {
+ let mut cmd = if cfg!(target_os = "windows") {
+ let mut cmd = Command::new("cmd");
+ cmd.arg("/C").arg("echo abc");
+ cmd
+ } else {
+ let mut cmd = shell_cmd();
+ cmd.arg("-c").arg("echo abc");
+ cmd
+ };
+ cmd.stdin(Stdio::null());
+ cmd.stdout(Stdio::piped());
+ let child = cmd.spawn().unwrap();
+
+ let mut stdout = child.stdout.unwrap();
+ let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
+ let mut buf = BorrowedBuf::from(buf.as_mut_slice());
+ stdout.read_buf(buf.unfilled()).unwrap();
+
+ // ChildStdout::read_buf should omit buffer initialization.
+ if cfg!(target_os = "windows") {
+ assert_eq!(buf.filled(), b"abc\r\n");
+ assert_eq!(buf.init_len(), 5);
+ } else {
+ assert_eq!(buf.filled(), b"abc\n");
+ assert_eq!(buf.init_len(), 4);
+ };
+}
+
+#[test]
+#[cfg_attr(any(target_os = "vxworks"), ignore)]
fn test_process_status() {
let mut status = if cfg!(target_os = "windows") {
Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap()
diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs
index 7e85d6a06..8e9ea293c 100644
--- a/library/std/src/sync/lazy_lock.rs
+++ b/library/std/src/sync/lazy_lock.rs
@@ -26,7 +26,7 @@ union Data<T, F> {
/// # Examples
///
/// ```
-/// #![feature(once_cell)]
+/// #![feature(lazy_cell)]
///
/// use std::collections::HashMap;
///
@@ -54,7 +54,7 @@ union Data<T, F> {
/// // Some("Hoyten")
/// }
/// ```
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
pub struct LazyLock<T, F = fn() -> T> {
once: Once,
data: UnsafeCell<Data<T, F>>,
@@ -64,7 +64,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// Creates a new lazy value with the given initializing
/// function.
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[unstable(feature = "lazy_cell", issue = "109736")]
pub const fn new(f: F) -> LazyLock<T, F> {
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
}
@@ -76,7 +76,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
+ /// #![feature(lazy_cell)]
///
/// use std::sync::LazyLock;
///
@@ -86,7 +86,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// assert_eq!(&*lazy, &92);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[unstable(feature = "lazy_cell", issue = "109736")]
pub fn force(this: &LazyLock<T, F>) -> &T {
this.once.call_once(|| {
// SAFETY: `call_once` only runs this closure once, ever.
@@ -122,7 +122,7 @@ impl<T, F> LazyLock<T, F> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T, F> Drop for LazyLock<T, F> {
fn drop(&mut self) {
match self.once.state() {
@@ -135,7 +135,7 @@ impl<T, F> Drop for LazyLock<T, F> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
type Target = T;
@@ -145,7 +145,7 @@ impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: Default> Default for LazyLock<T> {
/// Creates a new lazy value using `Default` as the initializing function.
#[inline]
@@ -154,7 +154,7 @@ impl<T: Default> Default for LazyLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
@@ -166,13 +166,13 @@ impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
// to not impl `Sync` for `F`
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
// auto-derived `Send` impl is OK.
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
#[cfg(test)]
diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs
index 4edc95617..f6a7c0a9f 100644
--- a/library/std/src/sync/mod.rs
+++ b/library/std/src/sync/mod.rs
@@ -133,7 +133,9 @@
//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
//! most one thread at a time is able to access some data.
//!
-//! - [`Once`]: Used for thread-safe, one-time initialization of a
+//! - [`Once`]: Used for a thread-safe, one-time global initialization routine
+//!
+//! - [`OnceLock`]: Used for thread-safe, one-time initialization of a
//! global variable.
//!
//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
@@ -147,6 +149,7 @@
//! [`mpsc`]: crate::sync::mpsc
//! [`Mutex`]: crate::sync::Mutex
//! [`Once`]: crate::sync::Once
+//! [`OnceLock`]: crate::sync::OnceLock
//! [`RwLock`]: crate::sync::RwLock
#![stable(feature = "rust1", since = "1.0.0")]
@@ -172,9 +175,9 @@ pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
-#[unstable(feature = "once_cell", issue = "74465")]
+#[unstable(feature = "lazy_cell", issue = "109736")]
pub use self::lazy_lock::LazyLock;
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
pub use self::once_lock::OnceLock;
pub(crate) use self::remutex::{ReentrantMutex, ReentrantMutexGuard};
diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs
index c6bb09b04..492e21d9b 100644
--- a/library/std/src/sync/mpmc/array.rs
+++ b/library/std/src/sync/mpmc/array.rs
@@ -25,7 +25,8 @@ struct Slot<T> {
/// The current stamp.
stamp: AtomicUsize,
- /// The message in this slot.
+ /// The message in this slot. Either read out in `read` or dropped through
+ /// `discard_all_messages`.
msg: UnsafeCell<MaybeUninit<T>>,
}
@@ -439,14 +440,13 @@ impl<T> Channel<T> {
Some(self.cap)
}
- /// Disconnects the channel and wakes up all blocked senders and receivers.
+ /// Disconnects senders and wakes up all blocked receivers.
///
/// Returns `true` if this call disconnected the channel.
- pub(crate) fn disconnect(&self) -> bool {
+ pub(crate) fn disconnect_senders(&self) -> bool {
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
if tail & self.mark_bit == 0 {
- self.senders.disconnect();
self.receivers.disconnect();
true
} else {
@@ -454,6 +454,85 @@ impl<T> Channel<T> {
}
}
+ /// Disconnects receivers and wakes up all blocked senders.
+ ///
+ /// Returns `true` if this call disconnected the channel.
+ ///
+ /// # Safety
+ /// May only be called once upon dropping the last receiver. The
+ /// destruction of all other receivers must have been observed with acquire
+ /// ordering or stronger.
+ pub(crate) unsafe fn disconnect_receivers(&self) -> bool {
+ let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
+ let disconnected = if tail & self.mark_bit == 0 {
+ self.senders.disconnect();
+ true
+ } else {
+ false
+ };
+
+ self.discard_all_messages(tail);
+ disconnected
+ }
+
+ /// Discards all messages.
+ ///
+ /// `tail` should be the current (and therefore last) value of `tail`.
+ ///
+ /// # Panicking
+ /// If a destructor panics, the remaining messages are leaked, matching the
+ /// behaviour of the unbounded channel.
+ ///
+ /// # Safety
+ /// This method must only be called when dropping the last receiver. The
+ /// destruction of all other receivers must have been observed with acquire
+ /// ordering or stronger.
+ unsafe fn discard_all_messages(&self, tail: usize) {
+ debug_assert!(self.is_disconnected());
+
+ // Only receivers modify `head`, so since we are the last one,
+ // this value will not change and will not be observed (since
+ // no new messages can be sent after disconnection).
+ let mut head = self.head.load(Ordering::Relaxed);
+ let tail = tail & !self.mark_bit;
+
+ let backoff = Backoff::new();
+ loop {
+ // Deconstruct the head.
+ let index = head & (self.mark_bit - 1);
+ let lap = head & !(self.one_lap - 1);
+
+ // Inspect the corresponding slot.
+ debug_assert!(index < self.buffer.len());
+ let slot = unsafe { self.buffer.get_unchecked(index) };
+ let stamp = slot.stamp.load(Ordering::Acquire);
+
+ // If the stamp is ahead of the head by 1, we may drop the message.
+ if head + 1 == stamp {
+ head = if index + 1 < self.cap {
+ // Same lap, incremented index.
+ // Set to `{ lap: lap, mark: 0, index: index + 1 }`.
+ head + 1
+ } else {
+ // One lap forward, index wraps around to zero.
+ // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
+ lap.wrapping_add(self.one_lap)
+ };
+
+ unsafe {
+ (*slot.msg.get()).assume_init_drop();
+ }
+ // If the tail equals the head, that means the channel is empty.
+ } else if tail == head {
+ return;
+ // Otherwise, a sender is about to write into the slot, so we need
+ // to wait for it to update the stamp.
+ } else {
+ backoff.spin_heavy();
+ }
+ }
+ }
+
/// Returns `true` if the channel is disconnected.
pub(crate) fn is_disconnected(&self) -> bool {
self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
@@ -483,23 +562,3 @@ impl<T> Channel<T> {
head.wrapping_add(self.one_lap) == tail & !self.mark_bit
}
}
-
-impl<T> Drop for Channel<T> {
- fn drop(&mut self) {
- // Get the index of the head.
- let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1);
-
- // Loop over all slots that hold a message and drop them.
- for i in 0..self.len() {
- // Compute the index of the next slot holding a message.
- let index = if hix + i < self.cap { hix + i } else { hix + i - self.cap };
-
- unsafe {
- debug_assert!(index < self.buffer.len());
- let slot = self.buffer.get_unchecked_mut(index);
- let msg = &mut *slot.msg.get();
- msg.as_mut_ptr().drop_in_place();
- }
- }
- }
-}
diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs
index ec6c0726a..406a331a3 100644
--- a/library/std/src/sync/mpmc/list.rs
+++ b/library/std/src/sync/mpmc/list.rs
@@ -549,6 +549,18 @@ impl<T> Channel<T> {
let mut head = self.head.index.load(Ordering::Acquire);
let mut block = self.head.block.load(Ordering::Acquire);
+ // If we're going to be dropping messages we need to synchronize with initialization
+ if head >> SHIFT != tail >> SHIFT {
+ // The block can be null here only if a sender is in the process of initializing the
+ // channel while another sender managed to send a message by inserting it into the
+ // semi-initialized channel and advanced the tail.
+ // In that case, just wait until it gets initialized.
+ while block.is_null() {
+ backoff.spin_heavy();
+ block = self.head.block.load(Ordering::Acquire);
+ }
+ }
+
unsafe {
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
while head >> SHIFT != tail >> SHIFT {
diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs
index 7a602cecd..2068dda39 100644
--- a/library/std/src/sync/mpmc/mod.rs
+++ b/library/std/src/sync/mpmc/mod.rs
@@ -227,7 +227,7 @@ impl<T> Drop for Sender<T> {
fn drop(&mut self) {
unsafe {
match &self.flavor {
- SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()),
+ SenderFlavor::Array(chan) => chan.release(|c| c.disconnect_senders()),
SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()),
SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
}
@@ -403,7 +403,7 @@ impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
unsafe {
match &self.flavor {
- ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()),
+ ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect_receivers()),
ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()),
ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
}
diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs
index 9d2f92ffc..632709fd9 100644
--- a/library/std/src/sync/mpsc/sync_tests.rs
+++ b/library/std/src/sync/mpsc/sync_tests.rs
@@ -1,5 +1,6 @@
use super::*;
use crate::env;
+use crate::rc::Rc;
use crate::sync::mpmc::SendTimeoutError;
use crate::thread;
use crate::time::Duration;
@@ -656,3 +657,15 @@ fn issue_15761() {
repro()
}
}
+
+#[test]
+fn drop_unreceived() {
+ let (tx, rx) = sync_channel::<Rc<()>>(1);
+ let msg = Rc::new(());
+ let weak = Rc::downgrade(&msg);
+ assert!(tx.send(msg).is_ok());
+ drop(rx);
+ // Messages should be dropped immediately when the last receiver is destroyed.
+ assert!(weak.upgrade().is_none());
+ drop(tx);
+}
diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs
index 065045f44..b8fec6902 100644
--- a/library/std/src/sync/mutex.rs
+++ b/library/std/src/sync/mutex.rs
@@ -107,8 +107,8 @@ use crate::sys::locks as sys;
/// *guard += 1;
/// ```
///
-/// It is sometimes necessary to manually drop the mutex guard to unlock it
-/// sooner than the end of the enclosing scope.
+/// To unlock a mutex guard sooner than the end of the enclosing scope,
+/// either create an inner scope or drop the guard manually.
///
/// ```
/// use std::sync::{Arc, Mutex};
@@ -125,11 +125,18 @@ use crate::sys::locks as sys;
/// let res_mutex_clone = Arc::clone(&res_mutex);
///
/// threads.push(thread::spawn(move || {
-/// let mut data = data_mutex_clone.lock().unwrap();
-/// // This is the result of some important and long-ish work.
-/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
-/// data.push(result);
-/// drop(data);
+/// // Here we use a block to limit the lifetime of the lock guard.
+/// let result = {
+/// let mut data = data_mutex_clone.lock().unwrap();
+/// // This is the result of some important and long-ish work.
+/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
+/// data.push(result);
+/// result
+/// // The mutex guard gets dropped here, together with any other values
+/// // created in the critical section.
+/// };
+/// // The guard created here is a temporary dropped at the end of the statement, i.e.
+/// // the lock would not remain being held even if the thread did some additional work.
/// *res_mutex_clone.lock().unwrap() += result;
/// }));
/// });
@@ -146,6 +153,8 @@ use crate::sys::locks as sys;
/// // It's even more important here than in the threads because we `.join` the
/// // threads after that. If we had not dropped the mutex guard, a thread could
/// // be waiting forever for it, causing a deadlock.
+/// // As in the threads, a block could have been used instead of calling the
+/// // `drop` function.
/// drop(data);
/// // Here the mutex guard is not assigned to a variable and so, even if the
/// // scope does not end after this line, the mutex is still released: there is
@@ -160,6 +169,7 @@ use crate::sys::locks as sys;
///
/// assert_eq!(*res_mutex.lock().unwrap(), 800);
/// ```
+///
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")]
pub struct Mutex<T: ?Sized> {
diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs
index ed339ca5d..ab25a5bcc 100644
--- a/library/std/src/sync/once_lock.rs
+++ b/library/std/src/sync/once_lock.rs
@@ -14,8 +14,6 @@ use crate::sync::Once;
/// # Examples
///
/// ```
-/// #![feature(once_cell)]
-///
/// use std::sync::OnceLock;
///
/// static CELL: OnceLock<String> = OnceLock::new();
@@ -32,7 +30,7 @@ use crate::sync::Once;
/// assert!(value.is_some());
/// assert_eq!(value.unwrap().as_str(), "Hello, World!");
/// ```
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
pub struct OnceLock<T> {
once: Once,
// Whether or not the value is initialized is tracked by `once.is_completed()`.
@@ -40,8 +38,6 @@ pub struct OnceLock<T> {
/// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
///
/// ```compile_fail,E0597
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// struct A<'a>(&'a str);
@@ -63,7 +59,8 @@ impl<T> OnceLock<T> {
/// Creates a new empty cell.
#[inline]
#[must_use]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
+ #[rustc_const_stable(feature = "once_cell", since = "1.70.0")]
pub const fn new() -> OnceLock<T> {
OnceLock {
once: Once::new(),
@@ -77,7 +74,7 @@ impl<T> OnceLock<T> {
/// Returns `None` if the cell is empty, or being initialized. This
/// method never blocks.
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get(&self) -> Option<&T> {
if self.is_initialized() {
// Safe b/c checked is_initialized
@@ -91,7 +88,7 @@ impl<T> OnceLock<T> {
///
/// Returns `None` if the cell is empty. This method never blocks.
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.is_initialized() {
// Safe b/c checked is_initialized and we have a unique access
@@ -111,8 +108,6 @@ impl<T> OnceLock<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// static CELL: OnceLock<i32> = OnceLock::new();
@@ -129,7 +124,7 @@ impl<T> OnceLock<T> {
/// }
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn set(&self, value: T) -> Result<(), T> {
let mut value = Some(value);
self.get_or_init(|| value.take().unwrap());
@@ -158,8 +153,6 @@ impl<T> OnceLock<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// let cell = OnceLock::new();
@@ -169,7 +162,7 @@ impl<T> OnceLock<T> {
/// assert_eq!(value, &92);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
@@ -195,7 +188,7 @@ impl<T> OnceLock<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
+ /// #![feature(once_cell_try)]
///
/// use std::sync::OnceLock;
///
@@ -209,7 +202,7 @@ impl<T> OnceLock<T> {
/// assert_eq!(cell.get(), Some(&92))
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[unstable(feature = "once_cell_try", issue = "109737")]
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
@@ -236,8 +229,6 @@ impl<T> OnceLock<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// let cell: OnceLock<String> = OnceLock::new();
@@ -248,7 +239,7 @@ impl<T> OnceLock<T> {
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn into_inner(mut self) -> Option<T> {
self.take()
}
@@ -262,8 +253,6 @@ impl<T> OnceLock<T> {
/// # Examples
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// let mut cell: OnceLock<String> = OnceLock::new();
@@ -275,7 +264,7 @@ impl<T> OnceLock<T> {
/// assert_eq!(cell.get(), None);
/// ```
#[inline]
- #[unstable(feature = "once_cell", issue = "74465")]
+ #[stable(feature = "once_cell", since = "1.70.0")]
pub fn take(&mut self) -> Option<T> {
if self.is_initialized() {
self.once = Once::new();
@@ -344,17 +333,17 @@ impl<T> OnceLock<T> {
// scoped thread B, which fills the cell, which is
// then destroyed by A. That is, destructor observes
// a sent value.
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
unsafe impl<T: Send> Send for OnceLock<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
impl<T> const Default for OnceLock<T> {
/// Creates a new empty cell.
@@ -362,8 +351,6 @@ impl<T> const Default for OnceLock<T> {
/// # Example
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// fn main() {
@@ -376,7 +363,7 @@ impl<T> const Default for OnceLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
@@ -386,7 +373,7 @@ impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: Clone> Clone for OnceLock<T> {
#[inline]
fn clone(&self) -> OnceLock<T> {
@@ -401,15 +388,13 @@ impl<T: Clone> Clone for OnceLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T> From<T> for OnceLock<T> {
/// Create a new cell with its contents set to `value`.
///
/// # Example
///
/// ```
- /// #![feature(once_cell)]
- ///
/// use std::sync::OnceLock;
///
/// # fn main() -> Result<(), i32> {
@@ -430,7 +415,7 @@ impl<T> From<T> for OnceLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: PartialEq> PartialEq for OnceLock<T> {
#[inline]
fn eq(&self, other: &OnceLock<T>) -> bool {
@@ -438,10 +423,10 @@ impl<T: PartialEq> PartialEq for OnceLock<T> {
}
}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
impl<T: Eq> Eq for OnceLock<T> {}
-#[unstable(feature = "once_cell", issue = "74465")]
+#[stable(feature = "once_cell", since = "1.70.0")]
unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
#[inline]
fn drop(&mut self) {
diff --git a/library/std/src/sync/remutex.rs b/library/std/src/sync/remutex.rs
index 4c054da64..519ec2c32 100644
--- a/library/std/src/sync/remutex.rs
+++ b/library/std/src/sync/remutex.rs
@@ -35,7 +35,7 @@ use crate::sys::locks as sys;
/// `owner` can be checked by other threads that want to see if they already
/// hold the lock, so needs to be atomic. If it compares equal, we're on the
/// same thread that holds the mutex and memory access can use relaxed ordering
-/// since we're not dealing with multiple threads. If it compares unequal,
+/// since we're not dealing with multiple threads. If it's not equal,
/// synchronization is left to the mutex, making relaxed memory ordering for
/// the `owner` field fine in all cases.
pub struct ReentrantMutex<T> {
diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs
index 403a5e627..a5fcbdf39 100644
--- a/library/std/src/sys/common/alloc.rs
+++ b/library/std/src/sys/common/alloc.rs
@@ -22,6 +22,7 @@ pub const MIN_ALIGN: usize = 8;
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
+ target_arch = "loongarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64",
diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs
index 29fc0835d..2b8782ddf 100644
--- a/library/std/src/sys/common/mod.rs
+++ b/library/std/src/sys/common/mod.rs
@@ -12,6 +12,7 @@
pub mod alloc;
pub mod small_c_string;
+pub mod thread_local;
#[cfg(test)]
mod tests;
diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs
new file mode 100644
index 000000000..e229eb16a
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/fast_local.rs
@@ -0,0 +1,254 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+ thread_local_internals,
+ cfg_target_thread_local,
+ thread_local,
+ libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+ // used to generate the `LocalKey` value for const-initialized thread locals
+ (@key $t:ty, const $init:expr) => {{
+ #[cfg_attr(not(bootstrap), inline)]
+ #[deny(unsafe_op_in_unsafe_fn)]
+ unsafe fn __getit(
+ _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ const INIT_EXPR: $t = $init;
+ // If the platform has support for `#[thread_local]`, use it.
+ #[thread_local]
+ static mut VAL: $t = INIT_EXPR;
+
+ // If a dtor isn't needed we can do something "very raw" and
+ // just get going.
+ if !$crate::mem::needs_drop::<$t>() {
+ unsafe {
+ return $crate::option::Option::Some(&VAL)
+ }
+ }
+
+ // 0 == dtor not registered
+ // 1 == dtor registered, dtor not run
+ // 2 == dtor registered and is running or has run
+ #[thread_local]
+ static mut STATE: $crate::primitive::u8 = 0;
+
+ unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
+ let ptr = ptr as *mut $t;
+
+ unsafe {
+ $crate::debug_assert_eq!(STATE, 1);
+ STATE = 2;
+ $crate::ptr::drop_in_place(ptr);
+ }
+ }
+
+ unsafe {
+ match STATE {
+ // 0 == we haven't registered a destructor, so do
+ // so now.
+ 0 => {
+ $crate::thread::__LocalKeyInner::<$t>::register_dtor(
+ $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
+ destroy,
+ );
+ STATE = 1;
+ $crate::option::Option::Some(&VAL)
+ }
+ // 1 == the destructor is registered and the value
+ // is valid, so return the pointer.
+ 1 => $crate::option::Option::Some(&VAL),
+ // otherwise the destructor has already run, so we
+ // can't give access.
+ _ => $crate::option::Option::None,
+ }
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }};
+
+ // used to generate the `LocalKey` value for `thread_local!`
+ (@key $t:ty, $init:expr) => {
+ {
+ #[inline]
+ fn __init() -> $t { $init }
+
+ #[cfg_attr(not(bootstrap), inline)]
+ unsafe fn __getit(
+ init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ #[thread_local]
+ static __KEY: $crate::thread::__LocalKeyInner<$t> =
+ $crate::thread::__LocalKeyInner::<$t>::new();
+
+ // FIXME: remove the #[allow(...)] marker when macros don't
+ // raise warning for missing/extraneous unsafe blocks anymore.
+ // See https://github.com/rust-lang/rust/issues/74838.
+ #[allow(unused_unsafe)]
+ unsafe {
+ __KEY.get(move || {
+ if let $crate::option::Option::Some(init) = init {
+ if let $crate::option::Option::Some(value) = init.take() {
+ return value;
+ } else if $crate::cfg!(debug_assertions) {
+ $crate::unreachable!("missing default value");
+ }
+ }
+ __init()
+ })
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }
+ };
+ ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+ $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+ $crate::__thread_local_inner!(@key $t, $($init)*);
+ }
+}
+
+#[doc(hidden)]
+pub mod fast {
+ use super::super::lazy::LazyKeyInner;
+ use crate::cell::Cell;
+ use crate::sys::thread_local_dtor::register_dtor;
+ use crate::{fmt, mem, panic};
+
+ #[derive(Copy, Clone)]
+ enum DtorState {
+ Unregistered,
+ Registered,
+ RunningOrHasRun,
+ }
+
+ // This data structure has been carefully constructed so that the fast path
+ // only contains one branch on x86. That optimization is necessary to avoid
+ // duplicated tls lookups on OSX.
+ //
+ // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+ pub struct Key<T> {
+ // If `LazyKeyInner::get` returns `None`, that indicates either:
+ // * The value has never been initialized
+ // * The value is being recursively initialized
+ // * The value has already been destroyed or is being destroyed
+ // To determine which kind of `None`, check `dtor_state`.
+ //
+ // This is very optimizer friendly for the fast path - initialized but
+ // not yet dropped.
+ inner: LazyKeyInner<T>,
+
+ // Metadata to keep track of the state of the destructor. Remember that
+ // this variable is thread-local, not global.
+ dtor_state: Cell<DtorState>,
+ }
+
+ impl<T> fmt::Debug for Key<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Key").finish_non_exhaustive()
+ }
+ }
+
+ impl<T> Key<T> {
+ pub const fn new() -> Key<T> {
+ Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
+ }
+
+ // note that this is just a publicly-callable function only for the
+ // const-initialized form of thread locals, basically a way to call the
+ // free `register_dtor` function defined elsewhere in std.
+ pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ unsafe {
+ register_dtor(a, dtor);
+ }
+ }
+
+ pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+ // SAFETY: See the definitions of `LazyKeyInner::get` and
+ // `try_initialize` for more information.
+ //
+ // The caller must ensure no mutable references are ever active to
+ // the inner cell or the inner T when this is called.
+ // The `try_initialize` is dependant on the passed `init` function
+ // for this.
+ unsafe {
+ match self.inner.get() {
+ Some(val) => Some(val),
+ None => self.try_initialize(init),
+ }
+ }
+ }
+
+ // `try_initialize` is only called once per fast thread local variable,
+ // except in corner cases where thread_local dtors reference other
+ // thread_local's, or it is being recursively initialized.
+ //
+ // Macos: Inlining this function can cause two `tlv_get_addr` calls to
+ // be performed for every call to `Key::get`.
+ // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
+ #[inline(never)]
+ unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
+ // SAFETY: See comment above (this function doc).
+ if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
+ // SAFETY: See comment above (this function doc).
+ Some(unsafe { self.inner.initialize(init) })
+ } else {
+ None
+ }
+ }
+
+ // `try_register_dtor` is only called once per fast thread local
+ // variable, except in corner cases where thread_local dtors reference
+ // other thread_local's, or it is being recursively initialized.
+ unsafe fn try_register_dtor(&self) -> bool {
+ match self.dtor_state.get() {
+ DtorState::Unregistered => {
+ // SAFETY: dtor registration happens before initialization.
+ // Passing `self` as a pointer while using `destroy_value<T>`
+ // is safe because the function will build a pointer to a
+ // Key<T>, which is the type of self and so find the correct
+ // size.
+ unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
+ self.dtor_state.set(DtorState::Registered);
+ true
+ }
+ DtorState::Registered => {
+ // recursively initialized
+ true
+ }
+ DtorState::RunningOrHasRun => false,
+ }
+ }
+ }
+
+ unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
+ let ptr = ptr as *mut Key<T>;
+
+ // SAFETY:
+ //
+ // The pointer `ptr` has been built just above and comes from
+ // `try_register_dtor` where it is originally a Key<T> coming from `self`,
+ // making it non-NUL and of the correct type.
+ //
+ // Right before we run the user destructor be sure to set the
+ // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
+ // causes future calls to `get` to run `try_initialize_drop` again,
+ // which will now fail, and return `None`.
+ //
+ // Wrap the call in a catch to ensure unwinding is caught in the event
+ // a panic takes place in a destructor.
+ if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
+ let value = (*ptr).inner.take();
+ (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
+ drop(value);
+ })) {
+ rtabort!("thread local panicked on drop");
+ }
+ }
+}
diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs
new file mode 100644
index 000000000..1fee84a04
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/mod.rs
@@ -0,0 +1,109 @@
+//! The following module declarations are outside cfg_if because the internal
+//! `__thread_local_internal` macro does not seem to be exported properly when using cfg_if
+#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
+
+#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))]
+mod fast_local;
+#[cfg(all(
+ not(target_thread_local),
+ not(all(target_family = "wasm", not(target_feature = "atomics")))
+))]
+mod os_local;
+#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
+mod static_local;
+
+#[cfg(not(test))]
+cfg_if::cfg_if! {
+ if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
+ #[doc(hidden)]
+ pub use static_local::statik::Key;
+ } else if #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+ #[doc(hidden)]
+ pub use fast_local::fast::Key;
+ } else if #[cfg(all(not(target_thread_local), not(all(target_family = "wasm", not(target_feature = "atomics")))))] {
+ #[doc(hidden)]
+ pub use os_local::os::Key;
+ }
+}
+
+#[doc(hidden)]
+#[cfg(test)]
+pub use realstd::thread::__LocalKeyInner as Key;
+
+mod lazy {
+ use crate::cell::UnsafeCell;
+ use crate::hint;
+ use crate::mem;
+
+ pub struct LazyKeyInner<T> {
+ inner: UnsafeCell<Option<T>>,
+ }
+
+ impl<T> LazyKeyInner<T> {
+ pub const fn new() -> LazyKeyInner<T> {
+ LazyKeyInner { inner: UnsafeCell::new(None) }
+ }
+
+ pub unsafe fn get(&self) -> Option<&'static T> {
+ // SAFETY: The caller must ensure no reference is ever handed out to
+ // the inner cell nor mutable reference to the Option<T> inside said
+ // cell. This make it safe to hand a reference, though the lifetime
+ // of 'static is itself unsafe, making the get method unsafe.
+ unsafe { (*self.inner.get()).as_ref() }
+ }
+
+ /// The caller must ensure that no reference is active: this method
+ /// needs unique access.
+ pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
+ // Execute the initialization up front, *then* move it into our slot,
+ // just in case initialization fails.
+ let value = init();
+ let ptr = self.inner.get();
+
+ // SAFETY:
+ //
+ // note that this can in theory just be `*ptr = Some(value)`, but due to
+ // the compiler will currently codegen that pattern with something like:
+ //
+ // ptr::drop_in_place(ptr)
+ // ptr::write(ptr, Some(value))
+ //
+ // Due to this pattern it's possible for the destructor of the value in
+ // `ptr` (e.g., if this is being recursively initialized) to re-access
+ // TLS, in which case there will be a `&` and `&mut` pointer to the same
+ // value (an aliasing violation). To avoid setting the "I'm running a
+ // destructor" flag we just use `mem::replace` which should sequence the
+ // operations a little differently and make this safe to call.
+ //
+ // The precondition also ensures that we are the only one accessing
+ // `self` at the moment so replacing is fine.
+ unsafe {
+ let _ = mem::replace(&mut *ptr, Some(value));
+ }
+
+ // SAFETY: With the call to `mem::replace` it is guaranteed there is
+ // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
+ // will never be reached.
+ unsafe {
+ // After storing `Some` we want to get a reference to the contents of
+ // what we just stored. While we could use `unwrap` here and it should
+ // always work it empirically doesn't seem to always get optimized away,
+ // which means that using something like `try_with` can pull in
+ // panicking code and cause a large size bloat.
+ match *ptr {
+ Some(ref x) => x,
+ None => hint::unreachable_unchecked(),
+ }
+ }
+ }
+
+ /// The other methods hand out references while taking &self.
+ /// As such, callers of this method must ensure no `&` and `&mut` are
+ /// available and used at the same time.
+ #[allow(unused)]
+ pub unsafe fn take(&mut self) -> Option<T> {
+ // SAFETY: See doc comment for this method.
+ unsafe { (*self.inner.get()).take() }
+ }
+ }
+}
diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs
new file mode 100644
index 000000000..1442a397e
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/os_local.rs
@@ -0,0 +1,197 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+ thread_local_internals,
+ cfg_target_thread_local,
+ thread_local,
+ libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+ // used to generate the `LocalKey` value for const-initialized thread locals
+ (@key $t:ty, const $init:expr) => {{
+ #[cfg_attr(not(bootstrap), inline)]
+ #[deny(unsafe_op_in_unsafe_fn)]
+ unsafe fn __getit(
+ _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ const INIT_EXPR: $t = $init;
+
+ // On platforms without `#[thread_local]` we fall back to the
+ // same implementation as below for os thread locals.
+ #[inline]
+ const fn __init() -> $t { INIT_EXPR }
+ static __KEY: $crate::thread::__LocalKeyInner<$t> =
+ $crate::thread::__LocalKeyInner::new();
+ #[allow(unused_unsafe)]
+ unsafe {
+ __KEY.get(move || {
+ if let $crate::option::Option::Some(init) = _init {
+ if let $crate::option::Option::Some(value) = init.take() {
+ return value;
+ } else if $crate::cfg!(debug_assertions) {
+ $crate::unreachable!("missing initial value");
+ }
+ }
+ __init()
+ })
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }};
+
+ // used to generate the `LocalKey` value for `thread_local!`
+ (@key $t:ty, $init:expr) => {
+ {
+ #[inline]
+ fn __init() -> $t { $init }
+
+ // `#[inline] does not work on windows-gnu due to linking errors around dllimports.
+ // See https://github.com/rust-lang/rust/issues/109797.
+ #[cfg_attr(not(windows), inline)]
+ unsafe fn __getit(
+ init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ static __KEY: $crate::thread::__LocalKeyInner<$t> =
+ $crate::thread::__LocalKeyInner::new();
+
+ // FIXME: remove the #[allow(...)] marker when macros don't
+ // raise warning for missing/extraneous unsafe blocks anymore.
+ // See https://github.com/rust-lang/rust/issues/74838.
+ #[allow(unused_unsafe)]
+ unsafe {
+ __KEY.get(move || {
+ if let $crate::option::Option::Some(init) = init {
+ if let $crate::option::Option::Some(value) = init.take() {
+ return value;
+ } else if $crate::cfg!(debug_assertions) {
+ $crate::unreachable!("missing default value");
+ }
+ }
+ __init()
+ })
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }
+ };
+ ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+ $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+ $crate::__thread_local_inner!(@key $t, $($init)*);
+ }
+}
+
+#[doc(hidden)]
+pub mod os {
+ use super::super::lazy::LazyKeyInner;
+ use crate::cell::Cell;
+ use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
+ use crate::{fmt, marker, panic, ptr};
+
+ /// Use a regular global static to store this key; the state provided will then be
+ /// thread-local.
+ pub struct Key<T> {
+ // OS-TLS key that we'll use to key off.
+ os: OsStaticKey,
+ marker: marker::PhantomData<Cell<T>>,
+ }
+
+ impl<T> fmt::Debug for Key<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Key").finish_non_exhaustive()
+ }
+ }
+
+ unsafe impl<T> Sync for Key<T> {}
+
+ struct Value<T: 'static> {
+ inner: LazyKeyInner<T>,
+ key: &'static Key<T>,
+ }
+
+ impl<T: 'static> Key<T> {
+ #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
+ pub const fn new() -> Key<T> {
+ Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
+ }
+
+ /// It is a requirement for the caller to ensure that no mutable
+ /// reference is active when this method is called.
+ pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+ // SAFETY: See the documentation for this method.
+ let ptr = unsafe { self.os.get() as *mut Value<T> };
+ if ptr.addr() > 1 {
+ // SAFETY: the check ensured the pointer is safe (its destructor
+ // is not running) + it is coming from a trusted source (self).
+ if let Some(ref value) = unsafe { (*ptr).inner.get() } {
+ return Some(value);
+ }
+ }
+ // SAFETY: At this point we are sure we have no value and so
+ // initializing (or trying to) is safe.
+ unsafe { self.try_initialize(init) }
+ }
+
+ // `try_initialize` is only called once per os thread local variable,
+ // except in corner cases where thread_local dtors reference other
+ // thread_local's, or it is being recursively initialized.
+ unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
+ // SAFETY: No mutable references are ever handed out meaning getting
+ // the value is ok.
+ let ptr = unsafe { self.os.get() as *mut Value<T> };
+ if ptr.addr() == 1 {
+ // destructor is running
+ return None;
+ }
+
+ let ptr = if ptr.is_null() {
+ // If the lookup returned null, we haven't initialized our own
+ // local copy, so do that now.
+ let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
+ // SAFETY: At this point we are sure there is no value inside
+ // ptr so setting it will not affect anyone else.
+ unsafe {
+ self.os.set(ptr as *mut u8);
+ }
+ ptr
+ } else {
+ // recursive initialization
+ ptr
+ };
+
+ // SAFETY: ptr has been ensured as non-NUL just above an so can be
+ // dereferenced safely.
+ unsafe { Some((*ptr).inner.initialize(init)) }
+ }
+ }
+
+ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
+ // SAFETY:
+ //
+ // The OS TLS ensures that this key contains a null value when this
+ // destructor starts to run. We set it back to a sentinel value of 1 to
+ // ensure that any future calls to `get` for this thread will return
+ // `None`.
+ //
+ // Note that to prevent an infinite loop we reset it back to null right
+ // before we return from the destructor ourselves.
+ //
+ // Wrap the call in a catch to ensure unwinding is caught in the event
+ // a panic takes place in a destructor.
+ if let Err(_) = panic::catch_unwind(|| unsafe {
+ let ptr = Box::from_raw(ptr as *mut Value<T>);
+ let key = ptr.key;
+ key.os.set(ptr::invalid_mut(1));
+ drop(ptr);
+ key.os.set(ptr::null_mut());
+ }) {
+ rtabort!("thread local panicked on drop");
+ }
+ }
+}
diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs
new file mode 100644
index 000000000..ec4f2a12b
--- /dev/null
+++ b/library/std/src/sys/common/thread_local/static_local.rs
@@ -0,0 +1,115 @@
+#[doc(hidden)]
+#[macro_export]
+#[allow_internal_unstable(
+ thread_local_internals,
+ cfg_target_thread_local,
+ thread_local,
+ libstd_thread_internals
+)]
+#[allow_internal_unsafe]
+macro_rules! __thread_local_inner {
+ // used to generate the `LocalKey` value for const-initialized thread locals
+ (@key $t:ty, const $init:expr) => {{
+ #[inline] // see comments below
+ #[deny(unsafe_op_in_unsafe_fn)]
+ unsafe fn __getit(
+ _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ const INIT_EXPR: $t = $init;
+
+ // wasm without atomics maps directly to `static mut`, and dtors
+ // aren't implemented because thread dtors aren't really a thing
+ // on wasm right now
+ //
+ // FIXME(#84224) this should come after the `target_thread_local`
+ // block.
+ static mut VAL: $t = INIT_EXPR;
+ unsafe { $crate::option::Option::Some(&VAL) }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }};
+
+ // used to generate the `LocalKey` value for `thread_local!`
+ (@key $t:ty, $init:expr) => {
+ {
+ #[inline]
+ fn __init() -> $t { $init }
+ #[inline]
+ unsafe fn __getit(
+ init: $crate::option::Option<&mut $crate::option::Option<$t>>,
+ ) -> $crate::option::Option<&'static $t> {
+ static __KEY: $crate::thread::__LocalKeyInner<$t> =
+ $crate::thread::__LocalKeyInner::new();
+
+ // FIXME: remove the #[allow(...)] marker when macros don't
+ // raise warning for missing/extraneous unsafe blocks anymore.
+ // See https://github.com/rust-lang/rust/issues/74838.
+ #[allow(unused_unsafe)]
+ unsafe {
+ __KEY.get(move || {
+ if let $crate::option::Option::Some(init) = init {
+ if let $crate::option::Option::Some(value) = init.take() {
+ return value;
+ } else if $crate::cfg!(debug_assertions) {
+ $crate::unreachable!("missing default value");
+ }
+ }
+ __init()
+ })
+ }
+ }
+
+ unsafe {
+ $crate::thread::LocalKey::new(__getit)
+ }
+ }
+ };
+ ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
+ $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
+ $crate::__thread_local_inner!(@key $t, $($init)*);
+ }
+}
+
+/// On some targets like wasm there's no threads, so no need to generate
+/// thread locals and we can instead just use plain statics!
+#[doc(hidden)]
+pub mod statik {
+ use super::super::lazy::LazyKeyInner;
+ use crate::fmt;
+
+ pub struct Key<T> {
+ inner: LazyKeyInner<T>,
+ }
+
+ unsafe impl<T> Sync for Key<T> {}
+
+ impl<T> fmt::Debug for Key<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Key").finish_non_exhaustive()
+ }
+ }
+
+ impl<T> Key<T> {
+ pub const fn new() -> Key<T> {
+ Key { inner: LazyKeyInner::new() }
+ }
+
+ pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
+ // SAFETY: The caller must ensure no reference is ever handed out to
+ // the inner cell nor mutable reference to the Option<T> inside said
+ // cell. This make it safe to hand a reference, though the lifetime
+ // of 'static is itself unsafe, making the get method unsafe.
+ let value = unsafe {
+ match self.inner.get() {
+ Some(ref value) => value,
+ None => self.inner.initialize(init),
+ }
+ };
+
+ Some(value)
+ }
+ }
+}
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
index c966f2177..cf0b27176 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -202,7 +202,7 @@ impl OpenOptions {
create: false,
create_new: false,
// system-specific
- mode: 0x777,
+ mode: 0o777,
}
}
diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs
index b64c174b0..427d8ff6f 100644
--- a/library/std/src/sys/hermit/futex.rs
+++ b/library/std/src/sys/hermit/futex.rs
@@ -16,7 +16,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
let r = unsafe {
abi::futex_wait(
- futex.as_mut_ptr(),
+ futex.as_ptr(),
expected,
timespec.as_ref().map_or(null(), |t| t as *const abi::timespec),
abi::FUTEX_RELATIVE_TIMEOUT,
@@ -28,12 +28,12 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
#[inline]
pub fn futex_wake(futex: &AtomicU32) -> bool {
- unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 }
+ unsafe { abi::futex_wake(futex.as_ptr(), 1) > 0 }
}
#[inline]
pub fn futex_wake_all(futex: &AtomicU32) {
unsafe {
- abi::futex_wake(futex.as_mut_ptr(), i32::MAX);
+ abi::futex_wake(futex.as_ptr(), i32::MAX);
}
}
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index d34a4cfed..c7cb84667 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -15,7 +15,6 @@
#![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)]
-use crate::intrinsics;
use crate::os::raw::c_char;
pub mod alloc;
@@ -76,9 +75,18 @@ pub fn abort_internal() -> ! {
}
}
-// FIXME: just a workaround to test the system
pub fn hashmap_random_keys() -> (u64, u64) {
- (1, 2)
+ let mut buf = [0; 16];
+ let mut slice = &mut buf[..];
+ while !slice.is_empty() {
+ let res = cvt(unsafe { abi::read_entropy(slice.as_mut_ptr(), slice.len(), 0) })
+ .expect("failed to generate random hashmap keys");
+ slice = &mut slice[res as usize..];
+ }
+
+ let key1 = buf[..8].try_into().unwrap();
+ let key2 = buf[8..].try_into().unwrap();
+ (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
}
// This function is needed by the panic runtime. The symbol is named in
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
index 5fb6281aa..d6f64a297 100644
--- a/library/std/src/sys/hermit/net.rs
+++ b/library/std/src/sys/hermit/net.rs
@@ -1,7 +1,7 @@
#![allow(dead_code)]
use crate::cmp;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
@@ -146,18 +146,35 @@ impl Socket {
Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) }))
}
- fn recv_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<usize> {
- let ret =
- cvt(unsafe { netc::recv(self.0.as_raw_fd(), buf.as_mut_ptr(), buf.len(), flags) })?;
- Ok(ret as usize)
+ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> {
+ let ret = cvt(unsafe {
+ netc::recv(
+ self.0.as_raw_fd(),
+ buf.as_mut().as_mut_ptr() as *mut u8,
+ buf.capacity(),
+ flags,
+ )
+ })?;
+ unsafe {
+ buf.advance(ret as usize);
+ }
+ Ok(())
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, 0)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), 0)?;
+ Ok(buf.len())
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, netc::MSG_PEEK)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?;
+ Ok(buf.len())
+ }
+
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.recv_with_flags(buf, 0)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index c080c176a..e767b2866 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -76,3 +76,12 @@ cfg_if::cfg_if! {
pub mod c;
}
}
+
+cfg_if::cfg_if! {
+ // Fuchsia components default to full backtrace.
+ if #[cfg(target_os = "fuchsia")] {
+ pub const FULL_BACKTRACE_DEFAULT: bool = true;
+ } else {
+ pub const FULL_BACKTRACE_DEFAULT: bool = false;
+ }
+}
diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
index 0d934318c..01505e944 100644
--- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs
+++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs
@@ -3,7 +3,6 @@
use crate::arch::asm;
use crate::cell::UnsafeCell;
use crate::cmp;
-use crate::convert::TryInto;
use crate::mem;
use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut};
use crate::ptr::{self, NonNull};
diff --git a/library/std/src/sys/sgx/fd.rs b/library/std/src/sys/sgx/fd.rs
index e5dc5b5ad..0c02a1076 100644
--- a/library/std/src/sys/sgx/fd.rs
+++ b/library/std/src/sys/sgx/fd.rs
@@ -1,7 +1,7 @@
use fortanix_sgx_abi::Fd;
use super::abi::usercalls;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::sys::{AsInner, FromInner, IntoInner};
@@ -30,6 +30,10 @@ impl FileDesc {
usercalls::read(self.fd, &mut [IoSliceMut::new(buf)])
}
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|b| self.read(b), buf)
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
usercalls::read(self.fd, bufs)
}
diff --git a/library/std/src/sys/sgx/net.rs b/library/std/src/sys/sgx/net.rs
index 4c4cd7d1d..923be5eb9 100644
--- a/library/std/src/sys/sgx/net.rs
+++ b/library/std/src/sys/sgx/net.rs
@@ -1,6 +1,6 @@
use crate::error;
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs};
use crate::sync::Arc;
use crate::sys::fd::FileDesc;
@@ -144,6 +144,10 @@ impl TcpStream {
self.inner.inner.read(buf)
}
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.inner.read_buf(buf)
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.inner.read_vectored(bufs)
}
diff --git a/library/std/src/sys/solid/net.rs b/library/std/src/sys/solid/net.rs
index 1b98ef993..7d7bfae14 100644
--- a/library/std/src/sys/solid/net.rs
+++ b/library/std/src/sys/solid/net.rs
@@ -2,7 +2,7 @@ use super::abi;
use crate::{
cmp,
ffi::CStr,
- io::{self, ErrorKind, IoSlice, IoSliceMut},
+ io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut},
mem,
net::{Shutdown, SocketAddr},
ptr, str,
@@ -294,19 +294,30 @@ impl Socket {
self.0.duplicate().map(Socket)
}
- fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
let ret = cvt(unsafe {
- netc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+ netc::recv(self.0.raw(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags)
})?;
- Ok(ret as usize)
+ unsafe {
+ buf.advance(ret as usize);
+ }
+ Ok(())
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, 0)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), 0)?;
+ Ok(buf.len())
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, MSG_PEEK)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), MSG_PEEK)?;
+ Ok(buf.len())
+ }
+
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.recv_with_flags(buf, 0)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 9874af4d3..ce5c048f2 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -469,6 +469,15 @@ impl<'a> Read for &'a FileDesc {
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
(**self).read_buf(cursor)
}
+
+ fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ (**self).read_vectored(bufs)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ (**self).is_read_vectored()
+ }
}
impl AsInner<OwnedFd> for FileDesc {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 7566fafda..abef170dd 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -34,7 +34,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
target_os = "watchos",
))]
use crate::sys::weak::syscall;
-#[cfg(any(target_os = "android", target_os = "macos"))]
+#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))]
use crate::sys::weak::weak;
use libc::{c_int, mode_t};
@@ -43,6 +43,7 @@ use libc::{c_int, mode_t};
target_os = "macos",
target_os = "ios",
target_os = "watchos",
+ target_os = "solaris",
all(target_os = "linux", target_env = "gnu")
))]
use libc::c_char;
@@ -1497,8 +1498,8 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
// Android has `linkat` on newer versions, but we happen to know `link`
// always has the correct behavior, so it's here as well.
cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
- } else if #[cfg(target_os = "macos")] {
- // On MacOS, older versions (<=10.9) lack support for linkat while newer
+ } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] {
+ // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer
// versions have it. We want to use linkat if it is available, so we use weak!
// to check. `linkat` is preferable to `link` because it gives us a flag to
// specify how symlinks should be handled. We pass 0 as the flags argument,
@@ -1892,7 +1893,7 @@ mod remove_dir_impl {
// file descriptor is automatically closed by libc::closedir() now, so give up ownership
let new_parent_fd = dir_fd.into_raw_fd();
// a valid root is not needed because we do not call any functions involving the full path
- // of the DirEntrys.
+ // of the `DirEntry`s.
let dummy_root = PathBuf::new();
let inner = InnerReadDir { dirp, root: dummy_root };
Ok((ReadDir::new(inner), new_parent_fd))
diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs
index 8d5b54021..d310be6c7 100644
--- a/library/std/src/sys/unix/futex.rs
+++ b/library/std/src/sys/unix/futex.rs
@@ -273,8 +273,6 @@ pub mod zircon {
#[cfg(target_os = "fuchsia")]
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
- use crate::convert::TryFrom;
-
// Sleep forever if the timeout is longer than fits in a i64.
let deadline = timeout
.and_then(|d| {
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
index 73b9bef7e..16c8e0c0e 100644
--- a/library/std/src/sys/unix/kernel_copy.rs
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -17,11 +17,9 @@
//! Once it has obtained all necessary pieces and brought any wrapper types into a state where they
//! can be safely bypassed it will attempt to use the `copy_file_range(2)`,
//! `sendfile(2)` or `splice(2)` syscalls to move data directly between file descriptors.
-//! Since those syscalls have requirements that cannot be fully checked in advance and
-//! gathering additional information about file descriptors would require additional syscalls
-//! anyway it simply attempts to use them one after another (guided by inaccurate hints) to
-//! figure out which one works and falls back to the generic read-write copy loop if none of them
-//! does.
+//! Since those syscalls have requirements that cannot be fully checked in advance it attempts
+//! to use them one after another (guided by hints) to figure out which one works and
+//! falls back to the generic read-write copy loop if none of them does.
//! Once a working syscall is found for a pair of file descriptors it will be called in a loop
//! until the copy operation is completed.
//!
@@ -84,14 +82,10 @@ pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
/// The methods on this type only provide hints, due to `AsRawFd` and `FromRawFd` the inferred
/// type may be wrong.
enum FdMeta {
- /// We obtained the FD from a type that can contain any type of `FileType` and queried the metadata
- /// because it is cheaper than probing all possible syscalls (reader side)
Metadata(Metadata),
Socket,
Pipe,
- /// We don't have any metadata, e.g. because the original type was `File` which can represent
- /// any `FileType` and we did not query the metadata either since it did not seem beneficial
- /// (writer side)
+ /// We don't have any metadata because the stat syscall failed
NoneObtained,
}
@@ -131,6 +125,39 @@ impl FdMeta {
}
}
+/// Returns true either if changes made to the source after a sendfile/splice call won't become
+/// visible in the sink or the source has explicitly opted into such behavior (e.g. by splicing
+/// a file into a pipe, the pipe being the source in this case).
+///
+/// This will prevent File -> Pipe and File -> Socket splicing/sendfile optimizations to uphold
+/// the Read/Write API semantics of io::copy.
+///
+/// Note: This is not 100% airtight, the caller can use the RawFd conversion methods to turn a
+/// regular file into a TcpSocket which will be treated as a socket here without checking.
+fn safe_kernel_copy(source: &FdMeta, sink: &FdMeta) -> bool {
+ match (source, sink) {
+ // Data arriving from a socket is safe because the sender can't modify the socket buffer.
+ // Data arriving from a pipe is safe(-ish) because either the sender *copied*
+ // the bytes into the pipe OR explicitly performed an operation that enables zero-copy,
+ // thus promising not to modify the data later.
+ (FdMeta::Socket, _) => true,
+ (FdMeta::Pipe, _) => true,
+ (FdMeta::Metadata(meta), _)
+ if meta.file_type().is_fifo() || meta.file_type().is_socket() =>
+ {
+ true
+ }
+ // Data going into non-pipes/non-sockets is safe because the "later changes may become visible" issue
+ // only happens for pages sitting in send buffers or pipes.
+ (_, FdMeta::Metadata(meta))
+ if !meta.file_type().is_fifo() && !meta.file_type().is_socket() =>
+ {
+ true
+ }
+ _ => false,
+ }
+}
+
struct CopyParams(FdMeta, Option<RawFd>);
struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
@@ -186,7 +213,8 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
// So we just try and fallback if needed.
// If current file offsets + write sizes overflow it may also fail, we do not try to fix that and instead
// fall back to the generic copy loop.
- if input_meta.potential_sendfile_source() {
+ if input_meta.potential_sendfile_source() && safe_kernel_copy(&input_meta, &output_meta)
+ {
let result = sendfile_splice(SpliceMode::Sendfile, readfd, writefd, max_write);
result.update_take(reader);
@@ -197,7 +225,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
}
}
- if input_meta.maybe_fifo() || output_meta.maybe_fifo() {
+ if (input_meta.maybe_fifo() || output_meta.maybe_fifo())
+ && safe_kernel_copy(&input_meta, &output_meta)
+ {
let result = sendfile_splice(SpliceMode::Splice, readfd, writefd, max_write);
result.update_take(reader);
@@ -298,13 +328,13 @@ impl CopyRead for &File {
impl CopyWrite for File {
fn properties(&self) -> CopyParams {
- CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
}
}
impl CopyWrite for &File {
fn properties(&self) -> CopyParams {
- CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
}
}
@@ -401,13 +431,13 @@ impl CopyRead for StdinLock<'_> {
impl CopyWrite for StdoutLock<'_> {
fn properties(&self) -> CopyParams {
- CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
}
}
impl CopyWrite for StderrLock<'_> {
fn properties(&self) -> CopyParams {
- CopyParams(FdMeta::NoneObtained, Some(self.as_raw_fd()))
+ CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
}
}
diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs
index 3fe849e23..a524270e3 100644
--- a/library/std/src/sys/unix/kernel_copy/tests.rs
+++ b/library/std/src/sys/unix/kernel_copy/tests.rs
@@ -83,6 +83,48 @@ fn copies_append_mode_sink() -> Result<()> {
Ok(())
}
+#[test]
+fn dont_splice_pipes_from_files() -> Result<()> {
+ // splicing to a pipe and then modifying the source could lead to changes
+ // becoming visible in an unexpected order.
+
+ use crate::io::SeekFrom;
+ use crate::os::unix::fs::FileExt;
+ use crate::process::{ChildStdin, ChildStdout};
+ use crate::sys_common::FromInner;
+
+ let (read_end, write_end) = crate::sys::pipe::anon_pipe()?;
+
+ let mut read_end = ChildStdout::from_inner(read_end);
+ let mut write_end = ChildStdin::from_inner(write_end);
+
+ let tmp_path = tmpdir();
+ let file = tmp_path.join("to_be_modified");
+ let mut file =
+ crate::fs::OpenOptions::new().create_new(true).read(true).write(true).open(file)?;
+
+ const SZ: usize = libc::PIPE_BUF as usize;
+
+ // put data in page cache
+ let mut buf: [u8; SZ] = [0x01; SZ];
+ file.write_all(&buf).unwrap();
+
+ // copy page into pipe
+ file.seek(SeekFrom::Start(0)).unwrap();
+ assert!(io::copy(&mut file, &mut write_end).unwrap() == SZ as u64);
+
+ // modify file
+ buf[0] = 0x02;
+ file.write_at(&buf, 0).unwrap();
+
+ // read from pipe
+ read_end.read_exact(buf.as_mut_slice()).unwrap();
+
+ assert_eq!(buf[0], 0x01, "data in pipe should reflect the original, not later modifications");
+
+ Ok(())
+}
+
#[bench]
fn bench_file_to_file_copy(b: &mut test::Bencher) {
const BYTES: usize = 128 * 1024;
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 8e05b618d..573bfa658 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -1,6 +1,6 @@
use crate::cmp;
use crate::ffi::CStr;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
@@ -242,19 +242,35 @@ impl Socket {
self.0.duplicate().map(Socket)
}
- fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
let ret = cvt(unsafe {
- libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+ libc::recv(
+ self.as_raw_fd(),
+ buf.as_mut().as_mut_ptr() as *mut c_void,
+ buf.capacity(),
+ flags,
+ )
})?;
- Ok(ret as usize)
+ unsafe {
+ buf.advance(ret as usize);
+ }
+ Ok(())
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, 0)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), 0)?;
+ Ok(buf.len())
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, MSG_PEEK)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), MSG_PEEK)?;
+ Ok(buf.len())
+ }
+
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.recv_with_flags(buf, 0)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
@@ -427,6 +443,17 @@ impl Socket {
Ok(passcred != 0)
}
+ #[cfg(target_os = "freebsd")]
+ pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
+ setsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT, passcred as libc::c_int)
+ }
+
+ #[cfg(target_os = "freebsd")]
+ pub fn passcred(&self) -> io::Result<bool> {
+ let passcred: libc::c_int = getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?;
+ Ok(passcred != 0)
+ }
+
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as libc::c_int;
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 21b035fb3..a345af76f 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -7,7 +7,6 @@ mod tests;
use crate::os::unix::prelude::*;
-use crate::convert::TryFrom;
use crate::error::Error as StdError;
use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
@@ -115,7 +114,10 @@ pub fn set_errno(e: i32) {
/// Gets a detailed string description for the given error number.
pub fn error_string(errno: i32) -> String {
extern "C" {
- #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")]
+ #[cfg_attr(
+ all(any(target_os = "linux", target_env = "newlib"), not(target_env = "ohos")),
+ link_name = "__xpg_strerror_r"
+ )]
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int;
}
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
index a744d0ab6..dc17c9fac 100644
--- a/library/std/src/sys/unix/pipe.rs
+++ b/library/std/src/sys/unix/pipe.rs
@@ -1,4 +1,4 @@
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
@@ -49,6 +49,10 @@ impl AnonPipe {
self.0.read(buf)
}
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0.read_buf(buf)
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index d4c7e58b3..e45c380a0 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -166,7 +166,6 @@ impl Process {
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
- use crate::default::Default;
use crate::sys::process::zircon::*;
let mut proc_info: zx_info_process_t = Default::default();
@@ -199,7 +198,6 @@ impl Process {
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
- use crate::default::Default;
use crate::sys::process::zircon::*;
let mut proc_info: zx_info_process_t = Default::default();
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index a6fe07873..0f347ffab 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -20,7 +20,8 @@ pub fn hashmap_random_keys() -> (u64, u64) {
not(target_os = "netbsd"),
not(target_os = "fuchsia"),
not(target_os = "redox"),
- not(target_os = "vxworks")
+ not(target_os = "vxworks"),
+ not(target_os = "emscripten")
))]
mod imp {
use crate::fs::File;
@@ -174,7 +175,7 @@ mod imp {
}
}
-#[cfg(target_os = "openbsd")]
+#[cfg(any(target_os = "openbsd", target_os = "emscripten"))]
mod imp {
use crate::sys::os::errno;
diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs
index b3626c564..a26f20795 100644
--- a/library/std/src/sys/unix/stdio.rs
+++ b/library/std/src/sys/unix/stdio.rs
@@ -1,4 +1,4 @@
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::os::unix::io::FromRawFd;
use crate::sys::fd::FileDesc;
@@ -18,6 +18,10 @@ impl io::Read for Stdin {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) }
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_buf(buf) }
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) }
}
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 0f11de8f5..6f5358340 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -174,6 +174,34 @@ impl From<libc::timespec> for Timespec {
}
}
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ target_pointer_width = "32",
+ not(target_arch = "riscv32")
+))]
+#[repr(C)]
+pub(in crate::sys::unix) struct __timespec64 {
+ pub(in crate::sys::unix) tv_sec: i64,
+ #[cfg(target_endian = "big")]
+ _padding: i32,
+ pub(in crate::sys::unix) tv_nsec: i32,
+ #[cfg(target_endian = "little")]
+ _padding: i32,
+}
+
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ target_pointer_width = "32",
+ not(target_arch = "riscv32")
+))]
+impl From<__timespec64> for Timespec {
+ fn from(t: __timespec64) -> Timespec {
+ Timespec::new(t.tv_sec, t.tv_nsec.into())
+ }
+}
+
#[cfg(any(
all(target_os = "macos", any(not(target_arch = "aarch64"))),
target_os = "ios",
@@ -352,29 +380,23 @@ mod inner {
impl Timespec {
pub fn now(clock: libc::clockid_t) -> Timespec {
// Try to use 64-bit time in preparation for Y2038.
- #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))]
+ #[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ target_pointer_width = "32",
+ not(target_arch = "riscv32")
+ ))]
{
use crate::sys::weak::weak;
// __clock_gettime64 was added to 32-bit arches in glibc 2.34,
// and it handles both vDSO calls and ENOSYS fallbacks itself.
- weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int);
-
- #[repr(C)]
- struct __timespec64 {
- tv_sec: i64,
- #[cfg(target_endian = "big")]
- _padding: i32,
- tv_nsec: i32,
- #[cfg(target_endian = "little")]
- _padding: i32,
- }
+ weak!(fn __clock_gettime64(libc::clockid_t, *mut super::__timespec64) -> libc::c_int);
if let Some(clock_gettime64) = __clock_gettime64.get() {
let mut t = MaybeUninit::uninit();
cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
- let t = unsafe { t.assume_init() };
- return Timespec::new(t.tv_sec, t.tv_nsec as i64);
+ return Timespec::from(unsafe { t.assume_init() });
}
}
diff --git a/library/std/src/sys/unsupported/net.rs b/library/std/src/sys/unsupported/net.rs
index a5204a084..bbc52703f 100644
--- a/library/std/src/sys/unsupported/net.rs
+++ b/library/std/src/sys/unsupported/net.rs
@@ -1,5 +1,5 @@
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::sys::unsupported;
use crate::time::Duration;
@@ -39,6 +39,10 @@ impl TcpStream {
self.0
}
+ pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0
+ }
+
pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
}
diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs
index 0bba673b4..d7d8f297a 100644
--- a/library/std/src/sys/unsupported/pipe.rs
+++ b/library/std/src/sys/unsupported/pipe.rs
@@ -1,4 +1,4 @@
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
pub struct AnonPipe(!);
@@ -7,6 +7,10 @@ impl AnonPipe {
self.0
}
+ pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.0
+ }
+
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0
}
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
index 0b9c8e61d..191db4b60 100644
--- a/library/std/src/sys/wasi/fd.rs
+++ b/library/std/src/sys/wasi/fd.rs
@@ -2,7 +2,7 @@
#![allow(dead_code)]
use super::err2io;
-use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::net::Shutdown;
use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
@@ -46,6 +46,22 @@ impl WasiFd {
unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) }
}
+ pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
+ unsafe {
+ let bufs = [wasi::Iovec {
+ buf: buf.as_mut().as_mut_ptr() as *mut u8,
+ buf_len: buf.capacity(),
+ }];
+ match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) {
+ Ok(n) => {
+ buf.advance(n);
+ Ok(())
+ }
+ Err(e) => Err(err2io(e)),
+ }
+ }
+ }
+
pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) }
}
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index d4866bbc3..3a205267e 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -441,7 +441,7 @@ impl File {
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
- crate::io::default_read_buf(|buf| self.read(buf), cursor)
+ self.fd.read_buf(cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
index cf4ebba1a..59d94a368 100644
--- a/library/std/src/sys/wasi/net.rs
+++ b/library/std/src/sys/wasi/net.rs
@@ -3,7 +3,7 @@
use super::err2io;
use super::fd::WasiFd;
use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::unsupported;
@@ -91,6 +91,10 @@ impl TcpStream {
self.read_vectored(&mut [IoSliceMut::new(buf)])
}
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.socket().as_inner().read_buf(buf)
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.socket().as_inner().read(bufs)
}
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 30356fa85..43c0cdb65 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -11,10 +11,11 @@ use crate::fmt;
use crate::io;
use crate::num::NonZeroU16;
use crate::os::windows::prelude::*;
-use crate::path::PathBuf;
-use crate::sys::c;
+use crate::path::{Path, PathBuf};
+use crate::sys::path::get_long_path;
use crate::sys::process::ensure_no_nuls;
use crate::sys::windows::os::current_exe;
+use crate::sys::{c, to_u16s};
use crate::sys_common::wstr::WStrUnits;
use crate::vec;
@@ -311,7 +312,7 @@ pub(crate) fn make_bat_command_line(
/// Takes a path and tries to return a non-verbatim path.
///
/// This is necessary because cmd.exe does not support verbatim paths.
-pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
+pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
use crate::ptr;
use crate::sys::windows::fill_utf16_buf;
@@ -324,6 +325,8 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
const N: u16 = b'N' as _;
const C: u16 = b'C' as _;
+ let mut path = to_u16s(path)?;
+
// Early return if the path is too long to remove the verbatim prefix.
const LEGACY_MAX_PATH: usize = 260;
if path.len() > LEGACY_MAX_PATH {
@@ -337,7 +340,13 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
fill_utf16_buf(
|buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
|full_path: &[u16]| {
- if full_path == &path[4..path.len() - 1] { full_path.into() } else { path }
+ if full_path == &path[4..path.len() - 1] {
+ let mut path: Vec<u16> = full_path.into();
+ path.push(0);
+ path
+ } else {
+ path
+ }
},
)
},
@@ -350,7 +359,9 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
|buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
|full_path: &[u16]| {
if full_path == &path[6..path.len() - 1] {
- full_path.into()
+ let mut path: Vec<u16> = full_path.into();
+ path.push(0);
+ path
} else {
// Restore the 'C' in "UNC".
path[6] = b'C' as u16;
@@ -360,6 +371,6 @@ pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
)
},
// For everything else, leave the path unchanged.
- _ => Ok(path),
+ _ => get_long_path(path, false),
}
}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 5d150eca0..1f4092ad7 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -296,7 +296,6 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _;
pub const STATUS_PENDING: NTSTATUS = 0x103 as _;
pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
-pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
// Equivalent to the `NT_SUCCESS` C preprocessor macro.
// See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
@@ -1282,6 +1281,46 @@ extern "system" {
) -> NTSTATUS;
}
+#[link(name = "ntdll")]
+extern "system" {
+ pub fn NtCreateFile(
+ FileHandle: *mut HANDLE,
+ DesiredAccess: ACCESS_MASK,
+ ObjectAttributes: *const OBJECT_ATTRIBUTES,
+ IoStatusBlock: *mut IO_STATUS_BLOCK,
+ AllocationSize: *mut i64,
+ FileAttributes: ULONG,
+ ShareAccess: ULONG,
+ CreateDisposition: ULONG,
+ CreateOptions: ULONG,
+ EaBuffer: *mut c_void,
+ EaLength: ULONG,
+ ) -> NTSTATUS;
+ pub fn NtReadFile(
+ FileHandle: BorrowedHandle<'_>,
+ Event: HANDLE,
+ ApcRoutine: Option<IO_APC_ROUTINE>,
+ ApcContext: *mut c_void,
+ IoStatusBlock: &mut IO_STATUS_BLOCK,
+ Buffer: *mut crate::mem::MaybeUninit<u8>,
+ Length: ULONG,
+ ByteOffset: Option<&LARGE_INTEGER>,
+ Key: Option<&ULONG>,
+ ) -> NTSTATUS;
+ pub fn NtWriteFile(
+ FileHandle: BorrowedHandle<'_>,
+ Event: HANDLE,
+ ApcRoutine: Option<IO_APC_ROUTINE>,
+ ApcContext: *mut c_void,
+ IoStatusBlock: &mut IO_STATUS_BLOCK,
+ Buffer: *const u8,
+ Length: ULONG,
+ ByteOffset: Option<&LARGE_INTEGER>,
+ Key: Option<&ULONG>,
+ ) -> NTSTATUS;
+ pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> ULONG;
+}
+
// Functions that aren't available on every version of Windows that we support,
// but we still use them and just provide some form of a fallback implementation.
compat_fn_with_fallback! {
@@ -1322,52 +1361,6 @@ compat_fn_optional! {
compat_fn_with_fallback! {
pub static NTDLL: &CStr = ansi_str!("ntdll");
- pub fn NtCreateFile(
- FileHandle: *mut HANDLE,
- DesiredAccess: ACCESS_MASK,
- ObjectAttributes: *const OBJECT_ATTRIBUTES,
- IoStatusBlock: *mut IO_STATUS_BLOCK,
- AllocationSize: *mut i64,
- FileAttributes: ULONG,
- ShareAccess: ULONG,
- CreateDisposition: ULONG,
- CreateOptions: ULONG,
- EaBuffer: *mut c_void,
- EaLength: ULONG
- ) -> NTSTATUS {
- STATUS_NOT_IMPLEMENTED
- }
- pub fn NtReadFile(
- FileHandle: BorrowedHandle<'_>,
- Event: HANDLE,
- ApcRoutine: Option<IO_APC_ROUTINE>,
- ApcContext: *mut c_void,
- IoStatusBlock: &mut IO_STATUS_BLOCK,
- Buffer: *mut crate::mem::MaybeUninit<u8>,
- Length: ULONG,
- ByteOffset: Option<&LARGE_INTEGER>,
- Key: Option<&ULONG>
- ) -> NTSTATUS {
- STATUS_NOT_IMPLEMENTED
- }
- pub fn NtWriteFile(
- FileHandle: BorrowedHandle<'_>,
- Event: HANDLE,
- ApcRoutine: Option<IO_APC_ROUTINE>,
- ApcContext: *mut c_void,
- IoStatusBlock: &mut IO_STATUS_BLOCK,
- Buffer: *const u8,
- Length: ULONG,
- ByteOffset: Option<&LARGE_INTEGER>,
- Key: Option<&ULONG>
- ) -> NTSTATUS {
- STATUS_NOT_IMPLEMENTED
- }
- pub fn RtlNtStatusToDosError(
- Status: NTSTATUS
- ) -> ULONG {
- Status as ULONG
- }
pub fn NtCreateKeyedEvent(
KeyedEventHandle: LPHANDLE,
DesiredAccess: ACCESS_MASK,
diff --git a/library/std/src/sys/windows/c/errors.rs b/library/std/src/sys/windows/c/errors.rs
index 23dcc119d..ad8da19b6 100644
--- a/library/std/src/sys/windows/c/errors.rs
+++ b/library/std/src/sys/windows/c/errors.rs
@@ -12,7 +12,7 @@ pub const ERROR_RESOURCE_CALL_TIMED_OUT: DWORD = 5910;
pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: DWORD = 8014;
pub const DNS_ERROR_RECORD_TIMED_OUT: DWORD = 9705;
-// The followiung list was obtained from
+// The following list was obtained from
// `/usr/x86_64-w64-mingw32/include/winerror.h`
// in the Debian package
// mingw-w64_6.0.0-3_all.deb
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index d2c597664..956db577d 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -1236,7 +1236,17 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
}
pub fn stat(path: &Path) -> io::Result<FileAttr> {
- metadata(path, ReparsePoint::Follow)
+ match metadata(path, ReparsePoint::Follow) {
+ Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
+ if let Ok(attrs) = lstat(path) {
+ if !attrs.file_type().is_symlink() {
+ return Ok(attrs);
+ }
+ }
+ Err(err)
+ }
+ result => result,
+ }
}
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
@@ -1393,24 +1403,40 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
let f = File::open(junction, &opts)?;
let h = f.as_inner().as_raw_handle();
-
unsafe {
let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let data_ptr = data.0.as_mut_ptr();
+ let data_end = data_ptr.add(c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
// Zero the header to ensure it's fully initialized, including reserved parameters.
*db = mem::zeroed();
- let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
- let mut i = 0;
+ let reparse_target_slice = {
+ let buf_start = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
+ // Compute offset in bytes and then divide so that we round down
+ // rather than hit any UB (admittedly this arithmetic should work
+ // out so that this isn't necessary)
+ let buf_len_bytes = usize::try_from(data_end.byte_offset_from(buf_start)).unwrap();
+ let buf_len_wchars = buf_len_bytes / core::mem::size_of::<c::WCHAR>();
+ core::slice::from_raw_parts_mut(buf_start, buf_len_wchars)
+ };
+
// FIXME: this conversion is very hacky
- let v = br"\??\";
- let v = v.iter().map(|x| *x as u16);
- for c in v.chain(original.as_os_str().encode_wide()) {
- *buf.add(i) = c;
+ let iter = br"\??\"
+ .iter()
+ .map(|x| *x as u16)
+ .chain(original.as_os_str().encode_wide())
+ .chain(core::iter::once(0));
+ let mut i = 0;
+ for c in iter {
+ if i >= reparse_target_slice.len() {
+ return Err(crate::io::const_io_error!(
+ crate::io::ErrorKind::InvalidFilename,
+ "Input filename is too long"
+ ));
+ }
+ reparse_target_slice[i] = c;
i += 1;
}
- *buf.add(i) = 0;
- i += 1;
(*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
(*db).ReparseTargetMaximumLength = (i * 2) as c::WORD;
(*db).ReparseTargetLength = ((i - 1) * 2) as c::WORD;
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index ae33d48c6..b290f4070 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -327,7 +327,16 @@ impl<'a> Read for &'a Handle {
(**self).read(buf)
}
+ fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ (**self).read_buf(buf)
+ }
+
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(**self).read_vectored(bufs)
}
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ (**self).is_read_vectored()
+ }
}
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index e0701a498..ee1f5482b 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -1,7 +1,7 @@
#![unstable(issue = "none", feature = "windows_net")]
use crate::cmp;
-use crate::io::{self, IoSlice, IoSliceMut, Read};
+use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::windows::io::{
@@ -214,28 +214,38 @@ impl Socket {
Ok(Self(self.0.try_clone()?))
}
- fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
+ fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> {
// On unix when a socket is shut down all further reads return 0, so we
// do the same on windows to map a shut down socket to returning EOF.
- let length = cmp::min(buf.len(), i32::MAX as usize) as i32;
- let result =
- unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) };
+ let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32;
+ let result = unsafe {
+ c::recv(self.as_raw_socket(), buf.as_mut().as_mut_ptr() as *mut _, length, flags)
+ };
match result {
c::SOCKET_ERROR => {
let error = unsafe { c::WSAGetLastError() };
if error == c::WSAESHUTDOWN {
- Ok(0)
+ Ok(())
} else {
Err(io::Error::from_raw_os_error(error))
}
}
- _ => Ok(result as usize),
+ _ => {
+ unsafe { buf.advance(result as usize) };
+ Ok(())
+ }
}
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), 0)?;
+ Ok(buf.len())
+ }
+
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
self.recv_with_flags(buf, 0)
}
@@ -277,7 +287,9 @@ impl Socket {
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
- self.recv_with_flags(buf, c::MSG_PEEK)
+ let mut buf = BorrowedBuf::from(buf);
+ self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?;
+ Ok(buf.len())
}
fn recv_from_with_flags(
diff --git a/library/std/src/sys/windows/path.rs b/library/std/src/sys/windows/path.rs
index beeca1917..c3573d14c 100644
--- a/library/std/src/sys/windows/path.rs
+++ b/library/std/src/sys/windows/path.rs
@@ -220,6 +220,19 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
///
/// This path may or may not have a verbatim prefix.
pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
+ let path = to_u16s(path)?;
+ get_long_path(path, true)
+}
+
+/// Get a normalized absolute path that can bypass path length limits.
+///
+/// Setting prefer_verbatim to true suggests a stronger preference for verbatim
+/// paths even when not strictly necessary. This allows the Windows API to avoid
+/// repeating our work. However, if the path may be given back to users or
+/// passed to other application then it's preferable to use non-verbatim paths
+/// when possible. Non-verbatim paths are better understood by users and handled
+/// by more software.
+pub(crate) fn get_long_path(mut path: Vec<u16>, prefer_verbatim: bool) -> io::Result<Vec<u16>> {
// Normally the MAX_PATH is 260 UTF-16 code units (including the NULL).
// However, for APIs such as CreateDirectory[1], the limit is 248.
//
@@ -243,7 +256,6 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
// \\?\UNC\
const UNC_PREFIX: &[u16] = &[SEP, SEP, QUERY, SEP, U, N, C, SEP];
- let mut path = to_u16s(path)?;
if path.starts_with(VERBATIM_PREFIX) || path.starts_with(NT_PREFIX) || path == &[0] {
// Early return for paths that are already verbatim or empty.
return Ok(path);
@@ -275,29 +287,34 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
|mut absolute| {
path.clear();
- // Secondly, add the verbatim prefix. This is easier here because we know the
- // path is now absolute and fully normalized (e.g. `/` has been changed to `\`).
- let prefix = match absolute {
- // C:\ => \\?\C:\
- [_, COLON, SEP, ..] => VERBATIM_PREFIX,
- // \\.\ => \\?\
- [SEP, SEP, DOT, SEP, ..] => {
- absolute = &absolute[4..];
- VERBATIM_PREFIX
- }
- // Leave \\?\ and \??\ as-is.
- [SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[],
- // \\ => \\?\UNC\
- [SEP, SEP, ..] => {
- absolute = &absolute[2..];
- UNC_PREFIX
- }
- // Anything else we leave alone.
- _ => &[],
- };
-
- path.reserve_exact(prefix.len() + absolute.len() + 1);
- path.extend_from_slice(prefix);
+ // Only prepend the prefix if needed.
+ if prefer_verbatim || absolute.len() + 1 >= LEGACY_MAX_PATH {
+ // Secondly, add the verbatim prefix. This is easier here because we know the
+ // path is now absolute and fully normalized (e.g. `/` has been changed to `\`).
+ let prefix = match absolute {
+ // C:\ => \\?\C:\
+ [_, COLON, SEP, ..] => VERBATIM_PREFIX,
+ // \\.\ => \\?\
+ [SEP, SEP, DOT, SEP, ..] => {
+ absolute = &absolute[4..];
+ VERBATIM_PREFIX
+ }
+ // Leave \\?\ and \??\ as-is.
+ [SEP, SEP, QUERY, SEP, ..] | [SEP, QUERY, QUERY, SEP, ..] => &[],
+ // \\ => \\?\UNC\
+ [SEP, SEP, ..] => {
+ absolute = &absolute[2..];
+ UNC_PREFIX
+ }
+ // Anything else we leave alone.
+ _ => &[],
+ };
+
+ path.reserve_exact(prefix.len() + absolute.len() + 1);
+ path.extend_from_slice(prefix);
+ } else {
+ path.reserve_exact(absolute.len() + 1);
+ }
path.extend_from_slice(absolute);
path.push(0);
},
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 7b25edaa5..0780b2958 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -1,7 +1,7 @@
use crate::os::windows::prelude::*;
use crate::ffi::OsStr;
-use crate::io::{self, IoSlice, IoSliceMut, Read};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::path::Path;
use crate::ptr;
@@ -252,6 +252,28 @@ impl AnonPipe {
}
}
+ pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
+ let result = unsafe {
+ let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD;
+ self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len)
+ };
+
+ match result {
+ // The special treatment of BrokenPipe is to deal with Windows
+ // pipe semantics, which yields this error when *reading* from
+ // a pipe after the other end has closed; we interpret that as
+ // EOF on the pipe.
+ Err(ref e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(()),
+ Err(e) => Err(e),
+ Ok(n) => {
+ unsafe {
+ buf.advance(n);
+ }
+ Ok(())
+ }
+ }
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 10bc949e1..1c73b64e2 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -266,11 +266,7 @@ impl Command {
let (program, mut cmd_str) = if is_batch_file {
(
command_prompt()?,
- args::make_bat_command_line(
- &args::to_user_path(program)?,
- &self.args,
- self.force_quotes_enabled,
- )?,
+ args::make_bat_command_line(&program, &self.args, self.force_quotes_enabled)?,
)
} else {
let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?;
@@ -410,7 +406,7 @@ fn resolve_exe<'a>(
if has_exe_suffix {
// The application name is a path to a `.exe` file.
// Let `CreateProcessW` figure out if it exists or not.
- return path::maybe_verbatim(Path::new(exe_path));
+ return args::to_user_path(Path::new(exe_path));
}
let mut path = PathBuf::from(exe_path);
@@ -422,7 +418,7 @@ fn resolve_exe<'a>(
// It's ok to use `set_extension` here because the intent is to
// remove the extension that was just added.
path.set_extension("");
- return path::maybe_verbatim(&path);
+ return args::to_user_path(&path);
}
} else {
ensure_no_nuls(exe_path)?;
@@ -510,7 +506,7 @@ where
/// Check if a file exists without following symlinks.
fn program_exists(path: &Path) -> Option<Vec<u16>> {
unsafe {
- let path = path::maybe_verbatim(path).ok()?;
+ let path = args::to_user_path(path).ok()?;
// Getting attributes using `GetFileAttributesW` does not follow symlinks
// and it will almost always be successful if the link exists.
// There are some exceptions for special system files (e.g. the pagefile)
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 85ecc1def..cb24caa1e 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -2,9 +2,8 @@
mod tests;
use crate::cmp;
-use crate::convert::{TryFrom, TryInto};
use crate::fmt;
-use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
+use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::ptr;
@@ -272,6 +271,10 @@ impl TcpStream {
self.inner.read(buf)
}
+ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
+ self.inner.read_buf(buf)
+ }
+
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.read_vectored(bufs)
}
diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs
index 575988ec7..15042fc3b 100644
--- a/library/std/src/sys_common/thread_parking/id.rs
+++ b/library/std/src/sys_common/thread_parking/id.rs
@@ -79,7 +79,7 @@ impl Parker {
park_timeout(dur, self.state.as_ptr().addr());
// Swap to ensure that we observe all state changes with acquire
// ordering, even if the state has been changed after the timeout
- // occured.
+ // occurred.
self.state.swap(EMPTY, Acquire);
}
}
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index cf7c2e05a..7fdf03acc 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -173,200 +173,6 @@ macro_rules! thread_local {
);
}
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
-#[macro_export]
-#[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)]
-#[allow_internal_unsafe]
-macro_rules! __thread_local_inner {
- // used to generate the `LocalKey` value for const-initialized thread locals
- (@key $t:ty, const $init:expr) => {{
- #[cfg_attr(not(windows), inline)] // see comments below
- #[deny(unsafe_op_in_unsafe_fn)]
- unsafe fn __getit(
- _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
- ) -> $crate::option::Option<&'static $t> {
- const INIT_EXPR: $t = $init;
-
- // wasm without atomics maps directly to `static mut`, and dtors
- // aren't implemented because thread dtors aren't really a thing
- // on wasm right now
- //
- // FIXME(#84224) this should come after the `target_thread_local`
- // block.
- #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
- {
- static mut VAL: $t = INIT_EXPR;
- unsafe { $crate::option::Option::Some(&VAL) }
- }
-
- // If the platform has support for `#[thread_local]`, use it.
- #[cfg(all(
- target_thread_local,
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
- ))]
- {
- #[thread_local]
- static mut VAL: $t = INIT_EXPR;
-
- // If a dtor isn't needed we can do something "very raw" and
- // just get going.
- if !$crate::mem::needs_drop::<$t>() {
- unsafe {
- return $crate::option::Option::Some(&VAL)
- }
- }
-
- // 0 == dtor not registered
- // 1 == dtor registered, dtor not run
- // 2 == dtor registered and is running or has run
- #[thread_local]
- static mut STATE: $crate::primitive::u8 = 0;
-
- unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) {
- let ptr = ptr as *mut $t;
-
- unsafe {
- $crate::debug_assert_eq!(STATE, 1);
- STATE = 2;
- $crate::ptr::drop_in_place(ptr);
- }
- }
-
- unsafe {
- match STATE {
- // 0 == we haven't registered a destructor, so do
- // so now.
- 0 => {
- $crate::thread::__FastLocalKeyInner::<$t>::register_dtor(
- $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8,
- destroy,
- );
- STATE = 1;
- $crate::option::Option::Some(&VAL)
- }
- // 1 == the destructor is registered and the value
- // is valid, so return the pointer.
- 1 => $crate::option::Option::Some(&VAL),
- // otherwise the destructor has already run, so we
- // can't give access.
- _ => $crate::option::Option::None,
- }
- }
- }
-
- // On platforms without `#[thread_local]` we fall back to the
- // same implementation as below for os thread locals.
- #[cfg(all(
- not(target_thread_local),
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
- ))]
- {
- #[inline]
- const fn __init() -> $t { INIT_EXPR }
- static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
- $crate::thread::__OsLocalKeyInner::new();
- #[allow(unused_unsafe)]
- unsafe {
- __KEY.get(move || {
- if let $crate::option::Option::Some(init) = _init {
- if let $crate::option::Option::Some(value) = init.take() {
- return value;
- } else if $crate::cfg!(debug_assertions) {
- $crate::unreachable!("missing initial value");
- }
- }
- __init()
- })
- }
- }
- }
-
- unsafe {
- $crate::thread::LocalKey::new(__getit)
- }
- }};
-
- // used to generate the `LocalKey` value for `thread_local!`
- (@key $t:ty, $init:expr) => {
- {
- #[inline]
- fn __init() -> $t { $init }
-
- // When reading this function you might ask "why is this inlined
- // everywhere other than Windows?", and that's a very reasonable
- // question to ask. The short story is that it segfaults rustc if
- // this function is inlined. The longer story is that Windows looks
- // to not support `extern` references to thread locals across DLL
- // boundaries. This appears to at least not be supported in the ABI
- // that LLVM implements.
- //
- // Because of this we never inline on Windows, but we do inline on
- // other platforms (where external references to thread locals
- // across DLLs are supported). A better fix for this would be to
- // inline this function on Windows, but only for "statically linked"
- // components. For example if two separately compiled rlibs end up
- // getting linked into a DLL then it's fine to inline this function
- // across that boundary. It's only not fine to inline this function
- // across a DLL boundary. Unfortunately rustc doesn't currently
- // have this sort of logic available in an attribute, and it's not
- // clear that rustc is even equipped to answer this (it's more of a
- // Cargo question kinda). This means that, unfortunately, Windows
- // gets the pessimistic path for now where it's never inlined.
- //
- // The issue of "should enable on Windows sometimes" is #84933
- #[cfg_attr(not(windows), inline)]
- unsafe fn __getit(
- init: $crate::option::Option<&mut $crate::option::Option<$t>>,
- ) -> $crate::option::Option<&'static $t> {
- #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
- static __KEY: $crate::thread::__StaticLocalKeyInner<$t> =
- $crate::thread::__StaticLocalKeyInner::new();
-
- #[thread_local]
- #[cfg(all(
- target_thread_local,
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
- ))]
- static __KEY: $crate::thread::__FastLocalKeyInner<$t> =
- $crate::thread::__FastLocalKeyInner::new();
-
- #[cfg(all(
- not(target_thread_local),
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
- ))]
- static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
- $crate::thread::__OsLocalKeyInner::new();
-
- // FIXME: remove the #[allow(...)] marker when macros don't
- // raise warning for missing/extraneous unsafe blocks anymore.
- // See https://github.com/rust-lang/rust/issues/74838.
- #[allow(unused_unsafe)]
- unsafe {
- __KEY.get(move || {
- if let $crate::option::Option::Some(init) = init {
- if let $crate::option::Option::Some(value) = init.take() {
- return value;
- } else if $crate::cfg!(debug_assertions) {
- $crate::unreachable!("missing default value");
- }
- }
- __init()
- })
- }
- }
-
- unsafe {
- $crate::thread::LocalKey::new(__getit)
- }
- }
- };
- ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
- $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
- $crate::__thread_local_inner!(@key $t, $($init)*);
- }
-}
-
/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).
#[stable(feature = "thread_local_try_with", since = "1.26.0")]
#[non_exhaustive]
@@ -779,376 +585,3 @@ impl<T: 'static> LocalKey<RefCell<T>> {
self.with(|cell| cell.replace(value))
}
}
-
-mod lazy {
- use crate::cell::UnsafeCell;
- use crate::hint;
- use crate::mem;
-
- pub struct LazyKeyInner<T> {
- inner: UnsafeCell<Option<T>>,
- }
-
- impl<T> LazyKeyInner<T> {
- pub const fn new() -> LazyKeyInner<T> {
- LazyKeyInner { inner: UnsafeCell::new(None) }
- }
-
- pub unsafe fn get(&self) -> Option<&'static T> {
- // SAFETY: The caller must ensure no reference is ever handed out to
- // the inner cell nor mutable reference to the Option<T> inside said
- // cell. This make it safe to hand a reference, though the lifetime
- // of 'static is itself unsafe, making the get method unsafe.
- unsafe { (*self.inner.get()).as_ref() }
- }
-
- /// The caller must ensure that no reference is active: this method
- /// needs unique access.
- pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
- // Execute the initialization up front, *then* move it into our slot,
- // just in case initialization fails.
- let value = init();
- let ptr = self.inner.get();
-
- // SAFETY:
- //
- // note that this can in theory just be `*ptr = Some(value)`, but due to
- // the compiler will currently codegen that pattern with something like:
- //
- // ptr::drop_in_place(ptr)
- // ptr::write(ptr, Some(value))
- //
- // Due to this pattern it's possible for the destructor of the value in
- // `ptr` (e.g., if this is being recursively initialized) to re-access
- // TLS, in which case there will be a `&` and `&mut` pointer to the same
- // value (an aliasing violation). To avoid setting the "I'm running a
- // destructor" flag we just use `mem::replace` which should sequence the
- // operations a little differently and make this safe to call.
- //
- // The precondition also ensures that we are the only one accessing
- // `self` at the moment so replacing is fine.
- unsafe {
- let _ = mem::replace(&mut *ptr, Some(value));
- }
-
- // SAFETY: With the call to `mem::replace` it is guaranteed there is
- // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
- // will never be reached.
- unsafe {
- // After storing `Some` we want to get a reference to the contents of
- // what we just stored. While we could use `unwrap` here and it should
- // always work it empirically doesn't seem to always get optimized away,
- // which means that using something like `try_with` can pull in
- // panicking code and cause a large size bloat.
- match *ptr {
- Some(ref x) => x,
- None => hint::unreachable_unchecked(),
- }
- }
- }
-
- /// The other methods hand out references while taking &self.
- /// As such, callers of this method must ensure no `&` and `&mut` are
- /// available and used at the same time.
- #[allow(unused)]
- pub unsafe fn take(&mut self) -> Option<T> {
- // SAFETY: See doc comment for this method.
- unsafe { (*self.inner.get()).take() }
- }
- }
-}
-
-/// On some targets like wasm there's no threads, so no need to generate
-/// thread locals and we can instead just use plain statics!
-#[doc(hidden)]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-pub mod statik {
- use super::lazy::LazyKeyInner;
- use crate::fmt;
-
- pub struct Key<T> {
- inner: LazyKeyInner<T>,
- }
-
- unsafe impl<T> Sync for Key<T> {}
-
- impl<T> fmt::Debug for Key<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Key").finish_non_exhaustive()
- }
- }
-
- impl<T> Key<T> {
- pub const fn new() -> Key<T> {
- Key { inner: LazyKeyInner::new() }
- }
-
- pub unsafe fn get(&self, init: impl FnOnce() -> T) -> Option<&'static T> {
- // SAFETY: The caller must ensure no reference is ever handed out to
- // the inner cell nor mutable reference to the Option<T> inside said
- // cell. This make it safe to hand a reference, though the lifetime
- // of 'static is itself unsafe, making the get method unsafe.
- let value = unsafe {
- match self.inner.get() {
- Some(ref value) => value,
- None => self.inner.initialize(init),
- }
- };
-
- Some(value)
- }
- }
-}
-
-#[doc(hidden)]
-#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))]
-pub mod fast {
- use super::lazy::LazyKeyInner;
- use crate::cell::Cell;
- use crate::sys::thread_local_dtor::register_dtor;
- use crate::{fmt, mem, panic};
-
- #[derive(Copy, Clone)]
- enum DtorState {
- Unregistered,
- Registered,
- RunningOrHasRun,
- }
-
- // This data structure has been carefully constructed so that the fast path
- // only contains one branch on x86. That optimization is necessary to avoid
- // duplicated tls lookups on OSX.
- //
- // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
- pub struct Key<T> {
- // If `LazyKeyInner::get` returns `None`, that indicates either:
- // * The value has never been initialized
- // * The value is being recursively initialized
- // * The value has already been destroyed or is being destroyed
- // To determine which kind of `None`, check `dtor_state`.
- //
- // This is very optimizer friendly for the fast path - initialized but
- // not yet dropped.
- inner: LazyKeyInner<T>,
-
- // Metadata to keep track of the state of the destructor. Remember that
- // this variable is thread-local, not global.
- dtor_state: Cell<DtorState>,
- }
-
- impl<T> fmt::Debug for Key<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Key").finish_non_exhaustive()
- }
- }
-
- impl<T> Key<T> {
- pub const fn new() -> Key<T> {
- Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) }
- }
-
- // note that this is just a publicly-callable function only for the
- // const-initialized form of thread locals, basically a way to call the
- // free `register_dtor` function defined elsewhere in std.
- pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- unsafe {
- register_dtor(a, dtor);
- }
- }
-
- pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
- // SAFETY: See the definitions of `LazyKeyInner::get` and
- // `try_initialize` for more information.
- //
- // The caller must ensure no mutable references are ever active to
- // the inner cell or the inner T when this is called.
- // The `try_initialize` is dependant on the passed `init` function
- // for this.
- unsafe {
- match self.inner.get() {
- Some(val) => Some(val),
- None => self.try_initialize(init),
- }
- }
- }
-
- // `try_initialize` is only called once per fast thread local variable,
- // except in corner cases where thread_local dtors reference other
- // thread_local's, or it is being recursively initialized.
- //
- // Macos: Inlining this function can cause two `tlv_get_addr` calls to
- // be performed for every call to `Key::get`.
- // LLVM issue: https://bugs.llvm.org/show_bug.cgi?id=41722
- #[inline(never)]
- unsafe fn try_initialize<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> {
- // SAFETY: See comment above (this function doc).
- if !mem::needs_drop::<T>() || unsafe { self.try_register_dtor() } {
- // SAFETY: See comment above (this function doc).
- Some(unsafe { self.inner.initialize(init) })
- } else {
- None
- }
- }
-
- // `try_register_dtor` is only called once per fast thread local
- // variable, except in corner cases where thread_local dtors reference
- // other thread_local's, or it is being recursively initialized.
- unsafe fn try_register_dtor(&self) -> bool {
- match self.dtor_state.get() {
- DtorState::Unregistered => {
- // SAFETY: dtor registration happens before initialization.
- // Passing `self` as a pointer while using `destroy_value<T>`
- // is safe because the function will build a pointer to a
- // Key<T>, which is the type of self and so find the correct
- // size.
- unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) };
- self.dtor_state.set(DtorState::Registered);
- true
- }
- DtorState::Registered => {
- // recursively initialized
- true
- }
- DtorState::RunningOrHasRun => false,
- }
- }
- }
-
- unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
- let ptr = ptr as *mut Key<T>;
-
- // SAFETY:
- //
- // The pointer `ptr` has been built just above and comes from
- // `try_register_dtor` where it is originally a Key<T> coming from `self`,
- // making it non-NUL and of the correct type.
- //
- // Right before we run the user destructor be sure to set the
- // `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
- // causes future calls to `get` to run `try_initialize_drop` again,
- // which will now fail, and return `None`.
- //
- // Wrap the call in a catch to ensure unwinding is caught in the event
- // a panic takes place in a destructor.
- if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
- let value = (*ptr).inner.take();
- (*ptr).dtor_state.set(DtorState::RunningOrHasRun);
- drop(value);
- })) {
- rtabort!("thread local panicked on drop");
- }
- }
-}
-
-#[doc(hidden)]
-#[cfg(all(
- not(target_thread_local),
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub mod os {
- use super::lazy::LazyKeyInner;
- use crate::cell::Cell;
- use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
- use crate::{fmt, marker, panic, ptr};
-
- /// Use a regular global static to store this key; the state provided will then be
- /// thread-local.
- pub struct Key<T> {
- // OS-TLS key that we'll use to key off.
- os: OsStaticKey,
- marker: marker::PhantomData<Cell<T>>,
- }
-
- impl<T> fmt::Debug for Key<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Key").finish_non_exhaustive()
- }
- }
-
- unsafe impl<T> Sync for Key<T> {}
-
- struct Value<T: 'static> {
- inner: LazyKeyInner<T>,
- key: &'static Key<T>,
- }
-
- impl<T: 'static> Key<T> {
- #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
- pub const fn new() -> Key<T> {
- Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
- }
-
- /// It is a requirement for the caller to ensure that no mutable
- /// reference is active when this method is called.
- pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
- // SAFETY: See the documentation for this method.
- let ptr = unsafe { self.os.get() as *mut Value<T> };
- if ptr.addr() > 1 {
- // SAFETY: the check ensured the pointer is safe (its destructor
- // is not running) + it is coming from a trusted source (self).
- if let Some(ref value) = unsafe { (*ptr).inner.get() } {
- return Some(value);
- }
- }
- // SAFETY: At this point we are sure we have no value and so
- // initializing (or trying to) is safe.
- unsafe { self.try_initialize(init) }
- }
-
- // `try_initialize` is only called once per os thread local variable,
- // except in corner cases where thread_local dtors reference other
- // thread_local's, or it is being recursively initialized.
- unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
- // SAFETY: No mutable references are ever handed out meaning getting
- // the value is ok.
- let ptr = unsafe { self.os.get() as *mut Value<T> };
- if ptr.addr() == 1 {
- // destructor is running
- return None;
- }
-
- let ptr = if ptr.is_null() {
- // If the lookup returned null, we haven't initialized our own
- // local copy, so do that now.
- let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
- // SAFETY: At this point we are sure there is no value inside
- // ptr so setting it will not affect anyone else.
- unsafe {
- self.os.set(ptr as *mut u8);
- }
- ptr
- } else {
- // recursive initialization
- ptr
- };
-
- // SAFETY: ptr has been ensured as non-NUL just above an so can be
- // dereferenced safely.
- unsafe { Some((*ptr).inner.initialize(init)) }
- }
- }
-
- unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
- // SAFETY:
- //
- // The OS TLS ensures that this key contains a null value when this
- // destructor starts to run. We set it back to a sentinel value of 1 to
- // ensure that any future calls to `get` for this thread will return
- // `None`.
- //
- // Note that to prevent an infinite loop we reset it back to null right
- // before we return from the destructor ourselves.
- //
- // Wrap the call in a catch to ensure unwinding is caught in the event
- // a panic takes place in a destructor.
- if let Err(_) = panic::catch_unwind(|| unsafe {
- let ptr = Box::from_raw(ptr as *mut Value<T>);
- let key = ptr.key;
- key.os.set(ptr::invalid_mut(1));
- drop(ptr);
- key.os.set(ptr::null_mut());
- }) {
- rtabort!("thread local panicked on drop");
- }
- }
-}
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 489af7767..13b845b25 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -131,7 +131,8 @@
//!
//! * Build the thread with [`Builder`] and pass the desired stack size to [`Builder::stack_size`].
//! * Set the `RUST_MIN_STACK` environment variable to an integer representing the desired stack
-//! size (in bytes). Note that setting [`Builder::stack_size`] will override this.
+//! size (in bytes). Note that setting [`Builder::stack_size`] will override this. Be aware that
+//! changes to `RUST_MIN_STACK` may be ignored after program start.
//!
//! Note that the stack size of the main thread is *not* determined by Rust.
//!
@@ -203,44 +204,9 @@ pub use self::local::{AccessError, LocalKey};
// by the elf linker. "static" is for single-threaded platforms where a global
// static is sufficient.
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
- target_thread_local,
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-#[doc(hidden)]
-pub use self::local::fast::Key as __FastLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
- target_thread_local,
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__FastLocalKeyInner;
-
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(not(test))]
-#[cfg(all(
- not(target_thread_local),
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
#[doc(hidden)]
-pub use self::local::os::Key as __OsLocalKeyInner;
-// when building for tests, use real std's type
-#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(test)]
-#[cfg(all(
- not(target_thread_local),
- not(all(target_family = "wasm", not(target_feature = "atomics"))),
-))]
-pub use realstd::thread::__OsLocalKeyInner;
-
#[unstable(feature = "libstd_thread_internals", issue = "none")]
-#[cfg(all(target_family = "wasm", not(target_feature = "atomics")))]
-#[doc(hidden)]
-pub use self::local::statik::Key as __StaticLocalKeyInner;
+pub use crate::sys::common::thread_local::Key as __LocalKeyInner;
////////////////////////////////////////////////////////////////////////////////
// Builder