diff options
Diffstat (limited to 'library/std')
160 files changed, 1886 insertions, 2398 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index da58d7c13bd..aa391a4b317 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2021" +autobenches = false [lib] crate-type = ["dylib", "rlib"] @@ -17,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.143" } +compiler_builtins = { version = "=0.1.145" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -130,6 +131,18 @@ name = "pipe-subprocess" path = "tests/pipe_subprocess.rs" harness = false +[[test]] +name = "sync" +path = "tests/sync/lib.rs" + +[[test]] +name = "floats" +path = "tests/floats/lib.rs" + +[[test]] +name = "thread_local" +path = "tests/thread_local/lib.rs" + [[bench]] name = "stdbenches" path = "benches/lib.rs" @@ -139,7 +152,8 @@ test = true level = "warn" check-cfg = [ 'cfg(bootstrap)', - 'cfg(target_arch, values("xtensa"))', + 'cfg(target_arch, values("xtensa", "aarch64-unknown-nto-qnx710_iosock", "x86_64-pc-nto-qnx710_iosock", "x86_64-pc-nto-qnx800","aarch64-unknown-nto-qnx800"))', + 'cfg(target_env, values("nto71_iosock", "nto80"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 1b21c230a0b..e749d9c0f79 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -5,3 +5,5 @@ extern crate test; mod hash; +mod path; +mod time; diff --git a/library/std/benches/path.rs b/library/std/benches/path.rs new file mode 100644 index 00000000000..094c00894a8 --- /dev/null +++ b/library/std/benches/path.rs @@ -0,0 +1,114 @@ +use core::hint::black_box; +use std::collections::{BTreeSet, HashSet}; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::*; + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { + let prefix = "my/home"; + let mut paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + paths.sort(); + + b.iter(|| { + black_box(paths.as_mut_slice()).sort_unstable(); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { + let prefix = "my/home"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(black_box(paths[500].as_path())) + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset_miss(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + let probe = PathBuf::from(prefix).join("other"); + + b.iter(|| set.remove(black_box(probe.as_path()))); +} + +#[bench] +fn bench_hash_path_short(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = Path::new("explorer.exe"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} + +#[bench] +fn bench_hash_path_long(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = + Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} diff --git a/library/std/benches/time.rs b/library/std/benches/time.rs new file mode 100644 index 00000000000..552481cad92 --- /dev/null +++ b/library/std/benches/time.rs @@ -0,0 +1,47 @@ +use std::time::Instant; + +#[cfg(not(target_arch = "wasm32"))] +use test::{Bencher, black_box}; + +macro_rules! bench_instant_threaded { + ($bench_name:ident, $thread_count:expr) => { + #[bench] + #[cfg(not(target_arch = "wasm32"))] + fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> { + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + + let running = Arc::new(AtomicBool::new(true)); + + let threads: Vec<_> = (0..$thread_count) + .map(|_| { + let flag = Arc::clone(&running); + std::thread::spawn(move || { + while flag.load(Ordering::Relaxed) { + black_box(Instant::now()); + } + }) + }) + .collect(); + + b.iter(|| { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); + }); + + running.store(false, Ordering::Relaxed); + + for t in threads { + t.join()?; + } + Ok(()) + } + }; +} + +bench_instant_threaded!(instant_contention_01_threads, 0); +bench_instant_threaded!(instant_contention_02_threads, 1); +bench_instant_threaded!(instant_contention_04_threads, 3); +bench_instant_threaded!(instant_contention_08_threads, 7); +bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/src/bstr.rs b/library/std/src/bstr.rs new file mode 100644 index 00000000000..dd491771628 --- /dev/null +++ b/library/std/src/bstr.rs @@ -0,0 +1,4 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +#[unstable(feature = "bstr", issue = "134915")] +pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index d2342d8fd51..6a0ff3a29e0 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -282,7 +282,7 @@ impl<K, V, S> HashMap<K, V, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -314,7 +314,7 @@ impl<K, V, S> HashMap<K, V, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -969,7 +969,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -979,13 +978,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -998,7 +997,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -1012,21 +1011,24 @@ where /// ``` /// /// ```should_panic - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); /// libraries.insert("Athenæum".to_string(), 1807); /// /// // Duplicate keys panic! - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut<Q: ?Sized, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + #[doc(alias = "get_many_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub fn get_disjoint_mut<Q: ?Sized, const N: usize>( + &mut self, + ks: [&Q; N], + ) -> [Option<&'_ mut V>; N] where K: Borrow<Q>, Q: Hash + Eq, @@ -1040,7 +1042,7 @@ where /// Returns an array of length `N` with the results of each query. `None` will be used if /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). /// /// # Safety /// @@ -1052,7 +1054,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1062,13 +1063,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1081,7 +1082,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1089,8 +1090,9 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub unsafe fn get_many_unchecked_mut<Q: ?Sized, const N: usize>( + #[doc(alias = "get_many_unchecked_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub unsafe fn get_disjoint_unchecked_mut<Q: ?Sized, const N: usize>( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] @@ -1281,7 +1283,7 @@ impl<K, V, S> HashMap<K, V, S> where S: BuildHasher, { - /// Creates a raw entry builder for the HashMap. + /// Creates a raw entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and @@ -1296,13 +1298,13 @@ where /// * Using custom comparison logic without newtype wrappers /// /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, + /// to put the `HashMap` into an inconsistent state which, while memory-safe, /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes + /// This is because implementations of `HashMap` may need to recompute hashes /// when resizing, at which point only the keys are available. /// /// Raw entries give mutable access to the keys. This must not be used @@ -1318,7 +1320,7 @@ where RawEntryBuilderMut { map: self } } - /// Creates a raw immutable entry builder for the HashMap. + /// Creates a raw immutable entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index bbb6ca23521..c265d42d06a 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -374,7 +374,7 @@ impl<T, S> HashSet<T, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// @@ -406,7 +406,7 @@ impl<T, S> HashSet<T, S> { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 11a29cdae62..c665dfd3624 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -10,9 +10,6 @@ #![stable(feature = "env", since = "1.0.0")] -#[cfg(test)] -mod tests; - use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; @@ -336,7 +333,10 @@ impl Error for VarError { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To pass an environment variable to a child process, you can instead use [`Command::env`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env`]: crate::process::Command::env /// /// # Panics /// @@ -396,7 +396,12 @@ pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To prevent a child process from inheriting an environment variable, you can +/// instead use [`Command::env_remove`] or [`Command::env_clear`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env_remove`]: crate::process::Command::env_remove +/// [`Command::env_clear`]: crate::process::Command::env_clear /// /// # Panics /// diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs deleted file mode 100644 index d0217261068..00000000000 --- a/library/std/src/env/tests.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] -fn test_self_exe_path() { - let path = current_exe(); - assert!(path.is_ok()); - let path = path.unwrap(); - - // Hard to test this function - assert!(path.is_absolute()); -} - -#[test] -fn test() { - assert!((!Path::new("test-path").is_absolute())); - - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); -} - -#[test] -#[cfg(windows)] -fn split_paths_windows() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::<Vec<_>>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse(r#""""#, &mut [""])); - assert!(check_parse(";;", &mut ["", "", ""])); - assert!(check_parse(r"c:\", &mut [r"c:\"])); - assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); - assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); - assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); - assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); -} - -#[test] -#[cfg(unix)] -fn split_paths_unix() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::<Vec<_>>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse("::", &mut ["", "", ""])); - assert!(check_parse("/", &mut ["/"])); - assert!(check_parse("/:", &mut ["/", ""])); - assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); -} - -#[test] -#[cfg(unix)] -fn join_paths_unix() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); - assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); - assert!(join_paths(["/te:st"].iter().cloned()).is_err()); -} - -#[test] -#[cfg(windows)] -fn join_paths_windows() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); - assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); - assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); - assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); -} - -#[test] -fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()), - format!("{:?}", args()) - ); -} - -#[test] -fn args_os_debug() { - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()), - format!("{:?}", args_os()) - ); -} - -#[test] -fn vars_debug() { - assert_eq!( - format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()), - format!("{:?}", vars()) - ); -} - -#[test] -fn vars_os_debug() { - assert_eq!( - format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()), - format!("{:?}", vars_os()) - ); -} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b3e63aaf1c5..def5f984c88 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 4f37e18a8cd..89612fa7475 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; @@ -324,6 +321,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -347,8 +358,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 42cd6e3fe2a..cc523c93b4d 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -4,9 +4,6 @@ //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; @@ -324,6 +321,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -347,8 +358,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 438d77b1626..260c499b7f4 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ @@ -306,8 +303,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +327,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 9bb4bfbab2a..7af646f8cfd 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ @@ -306,8 +303,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +327,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 7fb57d41043..c4c8dbccd7a 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -203,8 +203,8 @@ impl OsString { self } - /// Converts the `OsString` into a byte slice. To convert the byte slice back into an - /// `OsString`, use the [`OsStr::from_encoded_bytes_unchecked`] function. + /// Converts the `OsString` into a byte vector. To convert the byte vector back into an + /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. /// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9b752ed1443..0871a9e22d3 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -629,9 +629,9 @@ impl File { /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then an exclusive lock is held. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -639,6 +639,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, @@ -671,19 +674,22 @@ impl File { self.inner.lock() } - /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired. /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then a shared lock is held. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, @@ -716,14 +722,18 @@ impl File { self.inner.lock_shared() } - /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// Try to acquire an exclusive advisory lock on the file. + /// + /// Returns `Ok(false)` if a different lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive + /// lock. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -731,6 +741,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and @@ -764,20 +777,25 @@ impl File { self.inner.try_lock() } - /// Acquire a shared advisory lock on the file. - /// Returns `Ok(false)` if the file is exclusively locked. + /// Try to acquire a shared (non-exclusive) advisory lock on the file. + /// + /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// However, if this method returns `Ok(true)`, then it has acquired a shared lock. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and @@ -813,7 +831,12 @@ impl File { /// Release all locks on the file. /// - /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// All locks are released when the file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed. This method allows releasing locks without + /// closing the file. + /// + /// If no lock is currently held via this file descriptor/handle, this method may return an + /// error, or may return successfully without taking any action. /// /// # Platform-specific behavior /// @@ -2284,8 +2307,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `unlink` function on Unix -/// and the `DeleteFile` function on Windows. +/// This function currently corresponds to the `unlink` function on Unix. +/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2529,6 +2552,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. /// /// # Examples /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 28f16da1ed8..8e307f57cf9 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -14,7 +14,7 @@ use crate::os::unix::fs::symlink as junction_point; use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; use crate::path::Path; use crate::sync::Arc; -use crate::sys_common::io::test::{TempDir, tmpdir}; +use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -1384,7 +1384,7 @@ fn file_try_clone() { } #[test] -#[cfg(not(windows))] +#[cfg(not(target_vendor = "win7"))] fn unlink_readonly() { let tmpdir = tmpdir(); let path = tmpdir.join("file"); diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 2e0eb6cdce6..25b1ece2745 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -126,6 +126,7 @@ mod io_benches { use crate::io::prelude::*; #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] // no /dev fn bench_copy_buf_reader(b: &mut Bencher) { let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); // use dyn to avoid specializations unrelated to readbuf diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index b2ffeb0f95d..606099c8bc6 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -153,7 +153,7 @@ impl<T> Cursor<T> { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -201,7 +201,7 @@ impl<T> Cursor<T> { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "CURRENT_RUSTC_VERSION")] pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 231c8712ebd..0ffad2c27a4 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,6 +310,8 @@ pub use self::error::RawOsError; pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; pub(crate) use self::stdio::attempt_print_to_stderr; @@ -330,7 +332,6 @@ pub use self::{ }; use crate::mem::take; use crate::ops::{Deref, DerefMut}; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; use crate::{cmp, fmt, slice, str, sys}; mod buffered; @@ -338,11 +339,12 @@ pub(crate) mod copy; mod cursor; mod error; mod impls; +mod pipe; pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub(crate) use stdio::cleanup; @@ -3251,251 +3253,3 @@ impl<B: BufRead> Iterator for Lines<B> { } } } - -/// Create anonymous pipe that is close-on-exec and blocking. -/// -/// # Behavior -/// -/// A pipe is a synchronous, unidirectional data channel between two or more processes, like an -/// interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular: -/// -/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. -/// * A write on a [`PipeWriter`] blocks when the pipe is full. -/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] -/// returns EOF. -/// * [`PipeReader`] can be shared, but only one process will consume the data in the pipe. -/// -/// # Capacity -/// -/// Pipe capacity is platform dependent. To quote the Linux [man page]: -/// -/// > Different implementations have different limits for the pipe capacity. Applications should -/// > not rely on a particular capacity: an application should be designed so that a reading process -/// > consumes data as soon as it is available, so that a writing process does not remain blocked. -/// -/// # Examples -/// -/// ```no_run -/// #![feature(anonymous_pipe)] -/// # #[cfg(miri)] fn main() {} -/// # #[cfg(not(miri))] -/// # fn main() -> std::io::Result<()> { -/// # use std::process::Command; -/// # use std::io::{Read, Write}; -/// let (ping_rx, mut ping_tx) = std::io::pipe()?; -/// let (mut pong_rx, pong_tx) = std::io::pipe()?; -/// -/// // Spawn a process that echoes its input. -/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; -/// -/// ping_tx.write_all(b"hello")?; -/// // Close to unblock echo_server's reader. -/// drop(ping_tx); -/// -/// let mut buf = String::new(); -/// // Block until echo_server's writer is closed. -/// pong_rx.read_to_string(&mut buf)?; -/// assert_eq!(&buf, "hello"); -/// -/// echo_server.wait()?; -/// # Ok(()) -/// # } -/// ``` -/// [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html -/// [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe -/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[inline] -pub fn pipe() -> Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) -} - -/// Read end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); - -/// Write end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); - -impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::fs; - /// # use std::io::Write; - /// # use std::process::Command; - /// const NUM_SLOT: u8 = 2; - /// const NUM_PROC: u8 = 5; - /// const OUTPUT: &str = "work.txt"; - /// - /// let mut jobs = vec![]; - /// let (reader, mut writer) = std::io::pipe()?; - /// - /// // Write NUM_SLOT characters the pipe. - /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; - /// - /// // Spawn several processes that read a character from the pipe, do some work, then - /// // write back to the pipe. When the pipe is empty, the processes block, so only - /// // NUM_SLOT processes can be working at any given time. - /// for _ in 0..NUM_PROC { - /// jobs.push( - /// Command::new("bash") - /// .args(["-c", - /// &format!( - /// "read -n 1\n\ - /// echo -n 'x' >> '{OUTPUT}'\n\ - /// echo -n '|'", - /// ), - /// ]) - /// .stdin(reader.try_clone()?) - /// .stdout(writer.try_clone()?) - /// .spawn()?, - /// ); - /// } - /// - /// // Wait for all jobs to finish. - /// for mut job in jobs { - /// job.wait()?; - /// } - /// - /// // Check our work and clean up. - /// let xs = fs::read_to_string(OUTPUT)?; - /// fs::remove_file(OUTPUT)?; - /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> Result<Self> { - self.0.try_clone().map(Self) - } -} - -impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::process::Command; - /// # use std::io::Read; - /// let (mut reader, writer) = std::io::pipe()?; - /// - /// // Spawn a process that writes to stdout and stderr. - /// let mut peer = Command::new("bash") - /// .args([ - /// "-c", - /// "echo -n foo\n\ - /// echo -n bar >&2" - /// ]) - /// .stdout(writer.try_clone()?) - /// .stderr(writer) - /// .spawn()?; - /// - /// // Read and check the result. - /// let mut msg = String::new(); - /// reader.read_to_string(&mut msg)?; - /// assert_eq!(&msg, "foobar"); - /// - /// peer.wait()?; - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> Result<Self> { - self.0.try_clone().map(Self) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Read for &PipeReader { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Read for PipeReader { - fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Write for &PipeWriter { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl Write for PipeWriter { - fn write(&mut self, buf: &[u8]) -> Result<usize> { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs new file mode 100644 index 00000000000..266c7bc9638 --- /dev/null +++ b/library/std/src/io/pipe.rs @@ -0,0 +1,260 @@ +use crate::io; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; + +/// Create an anonymous pipe. +/// +/// # Behavior +/// +/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is +/// typically used to communicate between two or more separate processes, as there are better, +/// faster ways to communicate within a single process. +/// +/// In particular: +/// +/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. +/// * A write on a [`PipeWriter`] blocks when the pipe is full. +/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] +/// returns EOF. +/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but +/// writes (above a target-specific threshold) may have their data interleaved. +/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any +/// given byte will only get consumed by one reader. There are no guarantees about data +/// interleaving. +/// * Portable applications cannot assume any atomicity of messages larger than a single byte. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `pipe` function on Unix and the +/// `CreatePipe` function on Windows. +/// +/// Note that this [may change in the future][changes]. +/// +/// # Capacity +/// +/// Pipe capacity is platform dependent. To quote the Linux [man page]: +/// +/// > Different implementations have different limits for the pipe capacity. Applications should +/// > not rely on a particular capacity: an application should be designed so that a reading process +/// > consumes data as soon as it is available, so that a writing process does not remain blocked. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(anonymous_pipe)] +/// # #[cfg(miri)] fn main() {} +/// # #[cfg(not(miri))] +/// # fn main() -> std::io::Result<()> { +/// use std::process::Command; +/// use std::io::{pipe, Read, Write}; +/// let (ping_rx, mut ping_tx) = pipe()?; +/// let (mut pong_rx, pong_tx) = pipe()?; +/// +/// // Spawn a process that echoes its input. +/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +/// +/// ping_tx.write_all(b"hello")?; +/// // Close to unblock echo_server's reader. +/// drop(ping_tx); +/// +/// let mut buf = String::new(); +/// // Block until echo_server's writer is closed. +/// pong_rx.read_to_string(&mut buf)?; +/// assert_eq!(&buf, "hello"); +/// +/// echo_server.wait()?; +/// # Ok(()) +/// # } +/// ``` +/// [changes]: io#platform-specific-behavior +/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::fs; + /// use std::io::{pipe, Write}; + /// use std::process::Command; + /// const NUM_SLOT: u8 = 2; + /// const NUM_PROC: u8 = 5; + /// const OUTPUT: &str = "work.txt"; + /// + /// let mut jobs = vec![]; + /// let (reader, mut writer) = pipe()?; + /// + /// // Write NUM_SLOT characters the pipe. + /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; + /// + /// // Spawn several processes that read a character from the pipe, do some work, then + /// // write back to the pipe. When the pipe is empty, the processes block, so only + /// // NUM_SLOT processes can be working at any given time. + /// for _ in 0..NUM_PROC { + /// jobs.push( + /// Command::new("bash") + /// .args(["-c", + /// &format!( + /// "read -n 1\n\ + /// echo -n 'x' >> '{OUTPUT}'\n\ + /// echo -n '|'", + /// ), + /// ]) + /// .stdin(reader.try_clone()?) + /// .stdout(writer.try_clone()?) + /// .spawn()?, + /// ); + /// } + /// + /// // Wait for all jobs to finish. + /// for mut job in jobs { + /// job.wait()?; + /// } + /// + /// // Check our work and clean up. + /// let xs = fs::read_to_string(OUTPUT)?; + /// fs::remove_file(OUTPUT)?; + /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::process::Command; + /// use std::io::{pipe, Read}; + /// let (mut reader, writer) = pipe()?; + /// + /// // Spawn a process that writes to stdout and stderr. + /// let mut peer = Command::new("bash") + /// .args([ + /// "-c", + /// "echo -n foo\n\ + /// echo -n bar >&2" + /// ]) + /// .stdout(writer.try_clone()?) + /// .stderr(writer) + /// .spawn()?; + /// + /// // Read and check the result. + /// let mut msg = String::new(); + /// reader.read_to_string(&mut msg)?; + /// assert_eq!(&msg, "foobar"); + /// + /// peer.wait()?; + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs new file mode 100644 index 00000000000..f113b157459 --- /dev/null +++ b/library/std/src/io/pipe/tests.rs @@ -0,0 +1,18 @@ +use crate::io::{Read, Write, pipe}; + +#[test] +#[cfg(all(any(unix, windows), not(miri)))] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index bf8f3a5adfb..e68d8c29fbc 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -159,7 +159,8 @@ where assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); - assert_eq!(*log.lock().unwrap(), [ - Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1 - ]); + assert_eq!( + *log.lock().unwrap(), + [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] + ); } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 85098b3bb18..f64f034cce7 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -7,7 +7,6 @@ use crate::mem::MaybeUninit; use crate::ops::Deref; #[test] -#[cfg_attr(target_os = "emscripten", ignore)] fn read_until() { let mut buf = Cursor::new(&b"12"[..]); let mut v = Vec::new(); @@ -359,7 +358,6 @@ fn chain_zero_length_read_is_not_eof() { } #[bench] -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { @@ -823,20 +821,3 @@ fn try_oom_error() { let io_err = io::Error::from(reserve_err); assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind()); } - -#[test] -#[cfg(all(windows, unix, not(miri)))] -fn pipe_creation_clone_and_rw() { - let (rx, tx) = std::io::pipe().unwrap(); - - tx.try_clone().unwrap().write_all(b"12345").unwrap(); - drop(tx); - - let mut rx2 = rx.try_clone().unwrap(); - drop(rx); - - let mut s = String::new(); - rx2.read_to_string(&mut s).unwrap(); - drop(rx2); - assert_eq!(s, "12345"); -} diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 0c526eafdf3..1d26bf37f4d 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2387,13 +2387,12 @@ mod async_keyword {} /// [`async`]: ../std/keyword.async.html mod await_keyword {} -// FIXME(dyn_compat_renaming): Update URL and link text. #[doc(keyword = "dyn")] // /// `dyn` is a prefix of a [trait object]'s type. /// /// The `dyn` keyword is used to highlight that calls to methods on the associated `Trait` -/// are [dynamically dispatched]. To use the trait this way, it must be 'dyn-compatible'[^1]. +/// are [dynamically dispatched]. To use the trait this way, it must be *dyn compatible*[^1]. /// /// Unlike generic parameters or `impl Trait`, the compiler does not know the concrete type that /// is being passed. That is, the type has been [erased]. @@ -2406,7 +2405,7 @@ mod await_keyword {} /// the function pointer and then that function pointer is called. /// /// See the Reference for more information on [trait objects][ref-trait-obj] -/// and [object safety][ref-obj-safety]. +/// and [dyn compatibility][ref-dyn-compat]. /// /// ## Trade-offs /// @@ -2419,9 +2418,9 @@ mod await_keyword {} /// [trait object]: ../book/ch17-02-trait-objects.html /// [dynamically dispatched]: https://en.wikipedia.org/wiki/Dynamic_dispatch /// [ref-trait-obj]: ../reference/types/trait-object.html -/// [ref-obj-safety]: ../reference/items/traits.html#object-safety +/// [ref-dyn-compat]: ../reference/items/traits.html#dyn-compatibility /// [erased]: https://en.wikipedia.org/wiki/Type_erasure -/// [^1]: Formerly known as 'object safe'. +/// [^1]: Formerly known as *object safe*. mod dyn_keyword {} #[doc(keyword = "union")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 39f234e4ba6..954a4182fbd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -320,6 +320,8 @@ // Library features (core): // tidy-alphabetical-start #![feature(array_chunks)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -528,6 +530,8 @@ pub use core::option; pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; +#[unstable(feature = "new_range_api", issue = "125687")] +pub use core::range; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] @@ -581,6 +585,8 @@ pub mod f64; pub mod thread; pub mod ascii; pub mod backtrace; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; pub mod env; pub mod error; @@ -733,27 +739,4 @@ mod sealed { #[cfg(test)] #[allow(dead_code)] // Not used in all configurations. -pub(crate) mod test_helpers { - /// Test-only replacement for `rand::thread_rng()`, which is unusable for - /// us, as we want to allow running stdlib tests on tier-3 targets which may - /// not have `getrandom` support. - /// - /// Does a bit of a song and dance to ensure that the seed is different on - /// each call (as some tests sadly rely on this), but doesn't try that hard. - /// - /// This is duplicated in the `core`, `alloc` test suites (as well as - /// `std`'s integration tests), but figuring out a mechanism to share these - /// seems far more painful than copy-pasting a 7 line function a couple - /// times, given that even under a perma-unstable feature, I don't think we - /// want to expose types from `rand` from `std`. - #[track_caller] - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = crate::hash::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} +pub(crate) mod test_helpers; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 1b0d7f3dbf2..e0f9f0bb5ce 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -372,18 +372,3 @@ macro_rules! dbg { ($($crate::dbg!($val)),+,) }; } - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e9..e8355cc31d7 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,8 +6,7 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::netc as c; -use crate::sys_common::net::LookupHost; +use crate::sys::net::{LookupHost, netc as c}; use crate::sys_common::{FromInner, IntoInner}; use crate::{io, iter, mem, option, slice, vec}; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d..9b68f872955 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -15,7 +15,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index d318d457f35..a5c3983cd89 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); +const BASE_PORT: u16 = 19600; pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } @@ -30,31 +31,3 @@ pub fn tsa<A: ToSocketAddrs>(a: A) -> Result<Vec<SocketAddr>, String> { Err(e) => Err(e.to_string()), } } - -// The bots run multiple builds at the same time, and these builds -// all want to use ports. This function figures out which workspace -// it is running in and assigns a port range based on it. -fn base_port() -> u16 { - let cwd = if cfg!(target_env = "sgx") { - String::from("sgx") - } else { - env::current_dir().unwrap().into_os_string().into_string().unwrap() - }; - let dirs = [ - "32-opt", - "32-nopt", - "musl-64-opt", - "cross-opt", - "64-opt", - "64-nopt", - "64-opt-vg", - "64-debug-opt", - "all-opt", - "snap3", - "dist", - "sgx", - ]; - dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16 - * 1000 - + 19600 -} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 674c5fb7d6e..3eb798ad34a 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -12,7 +12,8 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num.rs b/library/std/src/num.rs index d2f679e7dde..ffb8789c906 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -6,9 +6,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "int_error_matching", since = "1.55.0")] pub use core::num::IntErrorKind; #[stable(feature = "generic_nonzero", since = "1.79.0")] @@ -29,28 +26,3 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; - -#[cfg(test)] -use crate::fmt; -#[cfg(test)] -use crate::ops::{Add, Div, Mul, Rem, Sub}; - -/// Helper function for testing numeric operations -#[cfg(test)] -pub fn test_num<T>(ten: T, two: T) -where - T: PartialEq - + Add<Output = T> - + Sub<Output = T> - + Mul<Output = T> - + Div<Output = T> - + Rem<Output = T> - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f..34479ca0e19 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { @@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 22f5528248a..03dff94350d 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -19,11 +19,9 @@ use crate::sys_common::{AsInner, IntoInner}; use crate::{fs, io}; /// Raw file descriptors. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_os = "hermit")] pub type RawFd = i32; @@ -33,7 +31,6 @@ pub type RawFd = i32; /// This is only available on unix and WASI platforms and must be imported in /// order to call the method. Windows platforms have a corresponding /// `AsRawHandle` and `AsRawSocket` set of traits. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -67,7 +64,6 @@ pub trait AsRawFd { /// A trait to express the ability to construct an object from a raw file /// descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file @@ -112,7 +108,6 @@ pub trait FromRawFd { /// A trait to express the ability to consume an object and acquire ownership of /// its raw file descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "into_raw_os", since = "1.4.0")] pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b23..233bc885fc7 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index b8601b533fe..b8c3440542d 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,7 +48,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. @@ -387,7 +387,7 @@ macro_rules! impl_from_raw_fd { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } )*}; diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index 67f607bd468..db9621c8c20 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -3,7 +3,7 @@ use super::*; #[test] fn read_vectored_at() { let msg = b"preadv is working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { @@ -31,7 +31,7 @@ fn read_vectored_at() { #[test] fn write_vectored_at() { let msg = b"pwritev is not working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 21e2176185d..0398a535eb5 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,7 +7,7 @@ use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; use crate::thread; use crate::time::Duration; diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs deleted file mode 100644 index 930aca887e3..00000000000 --- a/library/std/src/os/wasi/io/fd.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Owned and borrowed file descriptors. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// Tests for this module -#[cfg(test)] -mod tests; - -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 4e123a1eec8..5f9a735db08 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -4,3 +4,7 @@ #[stable(feature = "io_safety_wasi", since = "1.65.0")] pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs deleted file mode 100644 index da3b36adad4..00000000000 --- a/library/std/src/os/wasi/io/raw.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! WASI-specific extensions to general I/O primitives. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// NOTE: despite the fact that this module is unstable, -// stable Rust had the capability to access the stable -// re-exported items from os::fd::raw through this -// unstable module. -// In PR #95956 the stability checker was changed to check -// all path segments of an item rather than just the last, -// which caused the aforementioned stable usage to regress -// (see issue #99502). -// As a result, the items in os::fd::raw were given the -// rustc_allowed_through_unstable_modules attribute. -// No regression tests were added to ensure this property, -// as CI is not configured to test wasm32-wasi. -// If this module is stabilized, -// you may want to remove those attributes -// (assuming no other unstable modules need them). -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/tests.rs index 418274752b0..418274752b0 100644 --- a/library/std/src/os/wasi/io/fd/tests.rs +++ b/library/std/src/os/wasi/io/tests.rs diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574..c0517fab950 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. @@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock)) } } } @@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock)) } } } @@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock)) } } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 0277b79b8b6..201274cf03a 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -590,10 +590,10 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { value_ptr: *const T, value_size: usize, ) -> Self { - self.attributes.insert(attribute, ProcThreadAttributeValue { - ptr: value_ptr.cast::<c_void>(), - size: value_size, - }); + self.attributes.insert( + attribute, + ProcThreadAttributeValue { ptr: value_ptr.cast::<c_void>(), size: value_size }, + ); self } diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs index 1b16849af03..345005bcc78 100644 --- a/library/std/src/os/xous/ffi/definitions.rs +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -126,36 +126,42 @@ impl From<i32> for Error { #[stable(feature = "rust1", since = "1.0.0")] impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", match self { - Error::NoError => "no error occurred", - Error::BadAlignment => "memory was not properly aligned", - Error::BadAddress => "an invalid address was supplied", - Error::OutOfMemory => "the process or service has run out of memory", - Error::MemoryInUse => "the requested address is in use", - Error::InterruptNotFound => "the requested interrupt does not exist on this platform", - Error::InterruptInUse => "the requested interrupt is currently in use", - Error::InvalidString => "the specified string was not formatted correctly", - Error::ServerExists => "a server with that address already exists", - Error::ServerNotFound => "the requetsed server could not be found", - Error::ProcessNotFound => "the target process does not exist", - Error::ProcessNotChild => "the requested operation can only be done on child processes", - Error::ProcessTerminated => "the target process has crashed", - Error::Timeout => "the requested operation timed out", - Error::InternalError => "an internal error occurred", - Error::ServerQueueFull => "the server has too many pending messages", - Error::ThreadNotAvailable => "the specified thread does not exist", - Error::UnhandledSyscall => "the kernel did not recognize that syscall", - Error::InvalidSyscall => "the syscall had incorrect parameters", - Error::ShareViolation => "an attempt was made to share memory twice", - Error::InvalidThread => "tried to resume a thread that was not ready", - Error::InvalidPid => "kernel attempted to use a pid that was not valid", - Error::AccessDenied => "no permission to perform the requested operation", - Error::UseBeforeInit => "attempt to use a service before initialization finished", - Error::DoubleFree => "the requested resource was freed twice", - Error::DebugInProgress => "kernel attempted to activate a thread being debugged", - Error::InvalidLimit => "process attempted to adjust an invalid limit", - Error::UnknownError => "an unknown error occurred", - }) + write!( + f, + "{}", + match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => + "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => + "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + } + ) } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index d649357a56d..153189b8b03 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -529,6 +529,3 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> { Err(new) => BacktraceStyle::from_u8(new), } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 7fd08a97f1f..97e17acadea 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -67,9 +67,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] -#[cfg(test)] -mod tests; - use core::clone::CloneToUninit; use crate::borrow::{Borrow, Cow}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 4ec328208f0..14e6c2715df 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -120,16 +120,6 @@ mod common; pub mod v1 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; } /// The 2015 version of the prelude of The Rust Standard Library. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index e0dd2e14817..fd0fd1cb755 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2318,14 +2318,10 @@ pub fn exit(code: i32) -> ! { /// Terminates the process in an abnormal fashion. /// /// The function will never return and will immediately terminate the current -/// process in a platform specific "abnormal" manner. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. -/// -/// Rust IO buffers (eg, from `BufWriter`) will not be flushed. -/// Likewise, C stdio buffers will (on most platforms) not be flushed. +/// process in a platform specific "abnormal" manner. As a consequence, +/// no destructors on the current stack or any other thread's stack +/// will be run, Rust IO buffers (eg, from `BufWriter`) will not be flushed, +/// and C stdio buffers will (on most platforms) not be flushed. /// /// This is in contrast to the default behavior of [`panic!`] which unwinds /// the current thread's stack and calls all destructors. diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index e8cbfe337bc..69273d863eb 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -391,145 +391,6 @@ fn test_interior_nul_in_env_value_is_error() { } } -/// Tests that process creation flags work by debugging a process. -/// Other creation flags make it hard or impossible to detect -/// behavioral changes in the process. -#[test] -#[cfg(windows)] -fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, INFINITE}; - #[repr(C)] - struct DEBUG_EVENT { - pub event_code: u32, - pub process_id: u32, - pub thread_id: u32, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; - fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; - } - - const DEBUG_PROCESS: u32 = 1; - const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; - const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - - let mut child = - Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); -} - -/// Tests proc thread attributes by spawning a process with a custom parent process, -/// then comparing the parent process ID with the expected parent process ID. -#[test] -#[cfg(windows)] -fn test_proc_thread_attributes() { - use crate::mem; - use crate::os::windows::io::AsRawHandle; - use crate::os::windows::process::{CommandExt, ProcThreadAttributeList}; - use crate::sys::c::{BOOL, CloseHandle, HANDLE}; - use crate::sys::cvt; - - #[repr(C)] - #[allow(non_snake_case)] - struct PROCESSENTRY32W { - dwSize: u32, - cntUsage: u32, - th32ProcessID: u32, - th32DefaultHeapID: usize, - th32ModuleID: u32, - cntThreads: u32, - th32ParentProcessID: u32, - pcPriClassBase: i32, - dwFlags: u32, - szExeFile: [u16; 260], - } - - extern "system" { - fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE; - fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - } - - const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - const TH32CS_SNAPPROCESS: u32 = 0x00000002; - - struct ProcessDropGuard(crate::process::Child); - - impl Drop for ProcessDropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - - let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap()); - - let mut child_cmd = Command::new("cmd"); - - let parent_process_handle = parent.0.as_raw_handle(); - - let mut attribute_list = ProcThreadAttributeList::build() - .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) - .finish() - .unwrap(); - - let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap()); - - let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; - - let mut process_entry = PROCESSENTRY32W { - dwSize: mem::size_of::<PROCESSENTRY32W>() as u32, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0; 260], - }; - - unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - - loop { - if child.0.id() == process_entry.th32ProcessID { - break; - } - unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - } - - unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap(); - - assert_eq!(parent.0.id(), process_entry.th32ParentProcessID); - - drop(child) -} - #[test] fn test_command_implements_send_sync() { fn take_send_sync_type<T: Send + Sync>(_: T) {} @@ -688,7 +549,7 @@ fn debug_print() { #[test] #[cfg(windows)] fn run_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); @@ -707,7 +568,7 @@ fn run_bat_script() { #[test] #[cfg(windows)] fn run_canonical_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 384369b0012..3a22a16cb16 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -32,9 +32,14 @@ use crate::{mem, panic, sys}; // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { + #[cfg(not(feature = "panic_immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } + #[cfg(feature = "panic_immediate_abort")] + { + let _ = format_args!($($t)*); + } } } diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 862753e4765..067ff66d9af 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; // FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available use crate::sync::{Condvar, Mutex}; diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 98c83d8d326..78cf8841efe 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -350,6 +350,3 @@ unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {} impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {} #[stable(feature = "lazy_cell", since = "1.80.0")] impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {} - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 0cf4902d6d5..00966ee3ecf 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! return a `(Sender, Receiver)` tuple where the storage for pending //! messages is a pre-allocated buffer of a fixed size. All sends will be //! **synchronous** by blocking until there is buffer space available. Note //! that a bound of 0 is allowed, causing the channel to become a "rendezvous" @@ -360,9 +360,17 @@ impl<T> Sender<T> { /// that a return value of [`Err`] means that the data will never be /// received, but a return value of [`Ok`] does *not* mean that the data /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. + /// hang up immediately after this function returns [`Ok`]. However, if + /// the channel is zero-capacity, it acts as a rendezvous channel and a + /// return value of [`Ok`] means that the data has been received. /// - /// This method will never block the current thread. + /// If the channel is full and not disconnected, this call will block until + /// the send operation can proceed. If the channel becomes disconnected, + /// this call will wake up and return an error. The returned error contains + /// the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive + /// operation to appear on the other side of the channel. /// /// # Examples /// @@ -650,7 +658,7 @@ impl<T> fmt::Debug for Sender<T> { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Sender`] by cloning it. +/// Different threads can share this [`Receiver`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc.rs index c86b546e011..f942937c14d 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc.rs @@ -137,12 +137,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod sync_tests; - // MPSC channels are built as a wrapper around MPMC channels, which // were ported from the `crossbeam-channel` crate. MPMC channels are // not exposed publicly, but if you are curious about the implementation, @@ -737,9 +731,10 @@ impl<T> SyncSender<T> { // Attempts to send for a value on this receiver, returning an error if the // corresponding channel has hung up, or if it waits more than `timeout`. // - // This method is currently private and only used for tests. - #[allow(unused)] - fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> { + // This method is currently only used for tests. + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> { self.inner.send_timeout(t, timeout) } } diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 49f2dafd8fd..21e6b65a744 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,6 +13,9 @@ use crate::sync::Once; /// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock /// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// +/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock<T, F>`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -126,7 +129,7 @@ pub struct OnceLock<T> { } impl<T> OnceLock<T> { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -141,8 +144,8 @@ impl<T> OnceLock<T> { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -156,7 +159,8 @@ impl<T> OnceLock<T> { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. This method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -174,8 +178,6 @@ impl<T> OnceLock<T> { /// /// Waiting for a computation on another thread to finish: /// ```rust - /// #![feature(once_wait)] - /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -189,19 +191,20 @@ impl<T> OnceLock<T> { /// }) /// ``` #[inline] - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) -> &T { self.once.wait_force(); unsafe { self.get_unchecked() } } - /// Sets the contents of this cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. /// - /// Returns `Ok(())` if the cell's value was set by this call. + /// Returns `Ok(())` if the cell was uninitialized and + /// `Err(value)` if the cell was already initialized. /// /// # Examples /// @@ -230,13 +233,15 @@ impl<T> OnceLock<T> { } } - /// Sets the contents of this cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was uninitialized, + /// then returns a reference to it. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `try_insert` returns, though not necessarily the + /// one provided. /// - /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// Returns `Ok(&value)` if the cell was uninitialized and + /// `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -269,8 +274,8 @@ impl<T> OnceLock<T> { } } - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. + /// Gets the contents of the cell, initializing it to `f()` if the cell + /// was uninitialized. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -278,7 +283,7 @@ impl<T> OnceLock<T> { /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. The @@ -308,13 +313,13 @@ impl<T> OnceLock<T> { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. + /// it to `f()` if the cell was uninitialized. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -345,13 +350,13 @@ impl<T> OnceLock<T> { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. @@ -397,14 +402,14 @@ impl<T> OnceLock<T> { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// # Examples @@ -416,7 +421,7 @@ impl<T> OnceLock<T> { /// /// let mut cell: OnceLock<u32> = OnceLock::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -440,7 +445,7 @@ impl<T> OnceLock<T> { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was empty. + /// `None` if the cell was uninitialized. /// /// # Examples /// @@ -462,7 +467,7 @@ impl<T> OnceLock<T> { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -528,7 +533,7 @@ impl<T> OnceLock<T> { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -537,7 +542,7 @@ impl<T> OnceLock<T> { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -562,7 +567,7 @@ impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {} #[stable(feature = "once_cell", since = "1.70.0")] impl<T> Default for OnceLock<T> { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. /// /// # Example /// @@ -676,6 +681,3 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock<T> { } } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index a6e2389c93b..7f0f3f652bc 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 01ef71a187f..9362c764173 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; @@ -181,10 +178,29 @@ pub struct Mutex<T: ?Sized> { data: UnsafeCell<T>, } -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Send for Mutex<T> {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {} @@ -211,8 +227,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. #[stable(feature = "rust1", since = "1.0.0")] impl<T: ?Sized> !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {} diff --git a/library/std/src/sync/poison/once.rs b/library/std/src/sync/poison/once.rs index 27db4b634fb..d2938b7a0c1 100644 --- a/library/std/src/sync/poison/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -3,9 +3,6 @@ //! This primitive is meant to be used to run one-time initialization. An //! example use case would be for initializing an FFI library. -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; @@ -269,8 +266,6 @@ impl Once { /// # Example /// /// ```rust - /// #![feature(once_wait)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -289,7 +284,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -298,7 +293,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 1519baf99a8..f9d9321f5f2 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 0140e0d2129..e009eb410ef 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use cfg_if::cfg_if; use crate::cell::UnsafeCell; @@ -324,7 +321,10 @@ impl<T: ?Sized> ReentrantLock<T> { /// Otherwise, an RAII guard is returned. /// /// This function does not block. - pub(crate) fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> { + // FIXME maybe make it a public part of the API? + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> { let this_thread = current_id(); // Safety: We only touch lock_count when we own the inner mutex. // Additionally, we only call `self.owner.set()` while holding diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index a308fafc68b..53fbc9529e5 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -1,6 +1,6 @@ //! This is an implementation of a global allocator on wasm targets when -//! emscripten is not in use. In that situation there's no actual runtime for us -//! to lean on for allocation, so instead we provide our own! +//! emscripten or wasi is not in use. In that situation there's no actual runtime +//! for us to lean on for allocation, so instead we provide our own! //! //! The wasm instruction set has two instructions for getting the current //! amount of memory and growing the amount of memory. These instructions are the diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/io/io_slice/iovec.rs index 0424a1ac55a..072191315f7 100644 --- a/library/std/src/sys/pal/hermit/io.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,8 +1,13 @@ -use hermit_abi::{c_void, iovec}; +#[cfg(target_os = "hermit")] +use hermit_abi::iovec; +#[cfg(target_family = "unix")] +use libc::iovec; +use crate::ffi::c_void; use crate::marker::PhantomData; -use crate::os::hermit::io::{AsFd, AsRawFd}; use crate::slice; +#[cfg(target_os = "solid_asp3")] +use crate::sys::pal::abi::sockets::iovec; #[derive(Copy, Clone)] #[repr(transparent)] @@ -80,8 +85,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - hermit_abi::isatty(fd.as_raw_fd()) -} diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/io/io_slice/unsupported.rs index 604735d32d5..1572cac6cd7 100644 --- a/library/std/src/sys/pal/unsupported/io.rs +++ b/library/std/src/sys/io/io_slice/unsupported.rs @@ -50,7 +50,3 @@ impl<'a> IoSliceMut<'a> { self.0 } } - -pub fn is_terminal<T>(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/io/io_slice/wasi.rs index 57f81bc6257..87acbbd924e 100644 --- a/library/std/src/sys/pal/wasi/io.rs +++ b/library/std/src/sys/io/io_slice/wasi.rs @@ -1,7 +1,4 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; #[derive(Copy, Clone)] @@ -77,8 +74,3 @@ impl<'a> IoSliceMut<'a> { unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } } } - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/io/io_slice/windows.rs index 9ef4b7049b6..c3d8ec87c19 100644 --- a/library/std/src/sys/pal/solid/io.rs +++ b/library/std/src/sys/io/io_slice/windows.rs @@ -1,86 +1,82 @@ -use libc::c_void; - -use super::abi::sockets::iovec; use crate::marker::PhantomData; use crate::slice; +use crate::sys::c; #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a [u8]>, } impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSlice beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } } #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: iovec, + vec: c::WSABUF, _p: PhantomData<&'a mut [u8]>, } impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + assert!(buf.len() <= u32::MAX as usize); IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData, } } #[inline] pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { + if (self.vec.len as usize) < n { panic!("advancing IoSliceMut beyond its length"); } unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); } } #[inline] pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } } #[inline] pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } } } - -pub fn is_terminal<T>(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/io/is_terminal/hermit.rs b/library/std/src/sys/io/is_terminal/hermit.rs new file mode 100644 index 00000000000..61bdb6f0a54 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/hermit.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/is_terminal/isatty.rs b/library/std/src/sys/io/is_terminal/isatty.rs new file mode 100644 index 00000000000..6e0b46211b9 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/isatty.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/io/is_terminal/unsupported.rs b/library/std/src/sys/io/is_terminal/unsupported.rs new file mode 100644 index 00000000000..cee4add32fb --- /dev/null +++ b/library/std/src/sys/io/is_terminal/unsupported.rs @@ -0,0 +1,3 @@ +pub fn is_terminal<T>(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/io/is_terminal/windows.rs index f2865d2ffc1..3ec18fb47b9 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -1,90 +1,8 @@ -use core::ffi::c_void; - -use crate::marker::PhantomData; +use crate::ffi::c_void; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; -use crate::slice; use crate::sys::c; -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} - pub fn is_terminal(h: &impl AsHandle) -> bool { handle_is_console(h.as_handle()) } diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs new file mode 100644 index 00000000000..e00b479109f --- /dev/null +++ b/library/std/src/sys/io/mod.rs @@ -0,0 +1,44 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +mod io_slice { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3"))] { + mod iovec; + pub use iovec::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +mod is_terminal { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "wasi"))] { + mod isatty; + pub use isatty::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use io_slice::{IoSlice, IoSliceMut}; +pub use is_terminal::is_terminal; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47dece..1032fcba5e2 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,8 @@ pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; +pub mod io; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/net/connection/sgx.rs index c966886d163..b390a5eac5f 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,7 +1,7 @@ -use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; +use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys/net/connection/socket.rs index 74306978d22..6fe3430b53f 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -5,12 +5,32 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + +cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", @@ -24,11 +44,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 4e12374203e..42179dcc915 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -2,21 +2,20 @@ use core::ffi::c_int; -use super::fd::FileDesc; +pub(crate) use hermit_abi as netc; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +pub use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - +#[expect(non_camel_case_types)] pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/net/connection/socket/solid.rs index 5f6436807e2..f85ecbb883e 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -1,24 +1,23 @@ use libc::{c_int, c_void, size_t}; use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; pub mod netc { - pub use super::super::abi::sockets::*; + pub use crate::sys::abi::sockets::*; } +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - const fn max_iov() -> usize { // Judging by the source code, it's unlimited, but specify a lower // value just in case. @@ -78,7 +77,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { +pub fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -87,7 +86,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, @@ -268,17 +267,6 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } - pub fn write(&self, buf: &[u8]) -> io::Result<usize> { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { netc::writev( diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index fc236b8027b..fc236b8027b 100644 --- a/library/std/src/sys_common/net/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/net/connection/socket/unix.rs index d73b9fd5eb8..da631605527 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -5,8 +5,8 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } -pub use crate::sys::{cvt, cvt_r}; +pub(crate) use libc as netc; -#[allow(unused_extern_crates)] -pub extern crate libc as netc; +pub use crate::sys::{cvt, cvt_r}; +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; pub struct Socket(FileDesc); diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/net/connection/socket/wasip2.rs index f009a51821f..9d1c05a473e 100644 --- a/library/std/src/sys/pal/wasip2/net.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -6,8 +6,8 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem, str}; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/net/connection/socket/windows.rs index a92853c642c..80cf37eaf05 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,6 +110,7 @@ pub mod netc { } } +#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock<unsafe extern "system" fn() -> i32> = OnceLock::new(); @@ -400,12 +401,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -450,11 +451,11 @@ impl Socket { } None => 0, }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -487,26 +488,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result<Option<Duration>> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result<bool> { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result<Option<io::Error>> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 87e6106468f..87e6106468f 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/net/connection/unsupported.rs index fed95205027..87e6106468f 100644 --- a/library/std/src/sys/pal/teeos/net.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, @@ -365,5 +367,3 @@ pub mod netc { pub sin6_scope_id: u32, } } - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/net/connection/wasip1.rs index a6486799828..27e3a528af4 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -1,12 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use super::err2io; -use super::fd::WasiFd; use crate::fmt; 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; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index ff6e49ed2d4..ff6e49ed2d4 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index 3e18ed24208..3e18ed24208 100644 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index 640a02a64f5..640a02a64f5 100644 --- a/library/std/src/sys/pal/xous/net/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 572dd6b3b63..572dd6b3b63 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index 1b7ecac6d3a..1b7ecac6d3a 100644 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 00000000000..646679a1cc8 --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,41 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else if #[cfg(target_os = "uefi")] { + mod connection { + mod uefi; + pub use uefi::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c..3d555ad5050 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs index 28db4f8b679..d14035d9da4 100644 --- a/library/std/src/sys/pal/itron/time/tests.rs +++ b/library/std/src/sys/pal/itron/time/tests.rs @@ -8,24 +8,26 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::<Vec<_>>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM, 10000] + ); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::<Vec<_>>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::<Vec<_>>(), vec![42]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::<Vec<_>>(), + vec![abi::TMAX_RELTIM, 10000] + ); } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc..9a04fa4b97e 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -14,10 +14,7 @@ pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs index 2e680e740fd..e79a3d971c6 100644 --- a/library/std/src/sys/pal/sgx/stdio.rs +++ b/library/std/src/sys/pal/sgx/stdio.rs @@ -62,7 +62,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d..b399463c0c2 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be518..06af7bfade0 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -23,8 +23,6 @@ pub mod env; // `crate::sys::error` pub(crate) mod error; pub mod fs; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +49,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b19..f850fefc8f2 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -13,9 +13,6 @@ pub mod env; //pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 7504a0f7ad7..dccc137d6f5 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; +use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; +use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -278,6 +280,10 @@ impl OwnedDevicePath { pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } + + pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.0) + } } impl Drop for OwnedDevicePath { @@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath { impl crate::fmt::Debug for OwnedDevicePath { fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { - match device_path_to_text(self.0) { + match self.borrow().to_text() { Ok(p) => p.fmt(f), Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), } } } +pub(crate) struct BorrowedDevicePath<'a> { + protocol: NonNull<r_efi::protocols::device_path::Protocol>, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> BorrowedDevicePath<'a> { + pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) fn to_text(&self) -> io::Result<OsString> { + device_path_to_text(self.protocol) + } +} + +impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol<T> { guid: r_efi::efi::Guid, handle: NonNull<crate::ffi::c_void>, @@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> { None } + +/// Get device path protocol associated with shell mapping. +/// +/// returns None in case no such mapping is exists +pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDevicePath<'static>> { + let shell = + open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; + let mut path = os_string_to_raw(map.as_os_str()) + .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?; + + // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not + // freed throughout it's lifetime. So it has a 'static lifetime. + let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; + let protocol = NonNull::new(protocol) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; + + Ok(BorrowedDevicePath::new(protocol)) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb..4766e2ef0a9 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -17,10 +17,6 @@ pub mod args; pub mod env; pub mod fs; pub mod helpers; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134df..0757f1cb490 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec<OsString>, stdout: Option<Stdio>, stderr: Option<Stdio>, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -460,7 +490,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::<u16>()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +736,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,10 +750,37 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option<BTreeMap<EnvKey, (Option<OsString>, Option<OsString>)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::<EnvKey, (Option<OsString>, Option<OsString>)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option<OsString> = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index fdf011c1948..00cfa7a7fcf 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -9,9 +9,12 @@ use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ))] use libc::dirfd; +#[cfg(target_os = "fuchsia")] +use libc::fstatat as fstatat64; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( @@ -740,29 +743,27 @@ impl Iterator for ReadDir { // to `byte_offset` and thus does not require the full extent of `*entry_ptr` // to be in bounds of the same allocation, only the offset of the field // being referenced. - macro_rules! entry_field_ptr { - ($field:ident) => { - &raw const (*entry_ptr).$field - }; - } // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr(entry_field_ptr!(d_name).cast()); + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); let name_bytes = name.to_bytes(); if name_bytes == b"." || name_bytes == b".." { continue; } + // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as + // a value expression will do the right thing: `byte_offset` to the field and then + // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { - d_ino: *entry_field_ptr!(d_ino) as u64, + d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "nto", )))] - d_type: *entry_field_ptr!(d_type) as u8, + d_type: (*entry_ptr).d_type as u8, }; #[cfg(target_os = "vita")] @@ -850,7 +851,6 @@ impl Drop for Dir { target_os = "vita", target_os = "hurd", target_os = "espidf", - target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", target_os = "rtems", @@ -882,6 +882,7 @@ impl DirEntry { any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ), not(miri) // no dirfd on Miri @@ -910,6 +911,7 @@ impl DirEntry { not(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd", )), miri @@ -1213,6 +1215,7 @@ impl File { } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "android", target_os = "netbsd", @@ -1225,6 +1228,7 @@ impl File { } #[cfg(not(any( target_os = "android", + target_os = "fuchsia", target_os = "freebsd", target_os = "linux", target_os = "netbsd", @@ -1240,6 +1244,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1251,6 +1256,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1261,6 +1267,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1272,6 +1279,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1282,6 +1290,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1299,6 +1308,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1309,6 +1319,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1326,6 +1337,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1336,6 +1348,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1347,6 +1360,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs deleted file mode 100644 index 0d5a152dc0d..00000000000 --- a/library/std/src/sys/pal/unix/io.rs +++ /dev/null @@ -1,87 +0,0 @@ -use libc::{c_void, iovec}; - -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index 1350d743ff6..54d8f8ed2ed 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -2,7 +2,7 @@ use crate::fs::OpenOptions; use crate::io; use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write}; use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; #[test] fn copy_specialization() -> Result<()> { diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c514..00000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result<Socket> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<Socket> { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner<FileDesc> for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner<FileDesc> for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner<FileDesc> for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<TcpStream> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<TcpListener> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result<SocketAddr> { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result<UdpSocket> { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result<Option<Duration>> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result<bool> { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result<u32> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result<usize> { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner<Socket> for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option<SocketAddr> { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result<LookupHost> { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d00..027df6c5691 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -11,17 +11,10 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index ec4965c1d71..2bff192a5bd 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -19,8 +19,7 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { + if #[cfg(target_os = "nto")] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -187,12 +186,7 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any( - target_os = "watchos", - target_os = "tvos", - target_env = "nto70", - target_env = "nto71" - )))] + #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { cvt(libc::fork()) } @@ -201,8 +195,7 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - #[cfg(any(target_env = "nto70", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { use crate::sys::os::errno; diff --git a/library/std/src/sys/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs index 97e75f1b5b6..8c2f61a40de 100644 --- a/library/std/src/sys/pal/unix/stdio.rs +++ b/library/std/src/sys/pal/unix/stdio.rs @@ -92,7 +92,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(libc::EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option<impl io::Write> { Some(Stderr::new()) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index f657f82e6e3..356669980c7 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -130,7 +130,12 @@ impl Thread { } } - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly"))] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { cfg_if::cfg_if! { @@ -139,7 +144,7 @@ impl Thread { const TASK_COMM_LEN: usize = 16; let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); } else { - // FreeBSD and DragonFly BSD do not enforce length limits. + // FreeBSD, DragonFly, FreeBSD and NuttX do not enforce length limits. } }; // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, @@ -150,7 +155,7 @@ impl Thread { } } - #[cfg(any(target_os = "openbsd", target_os = "nuttx"))] + #[cfg(target_os = "openbsd")] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568..b1aaeb1b4c8 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -3,8 +3,6 @@ pub mod args; pub mod env; pub mod fs; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 5d54c790306..f4588a60ea9 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -1,8 +1,7 @@ //! System bindings for the wasm/web platform //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! OS level functionality for wasm. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,9 +20,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -46,5 +43,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs index ca49f871e19..d08b772e5fc 100644 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ b/library/std/src/sys/pal/wasi/stdio.rs @@ -101,7 +101,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9..72c9742b2e5 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -17,10 +17,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -#[path = "../wasi/io.rs"] -pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 8141bfac49a..32d59c4d0f7 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! or wasi wasm, so we have no runtime here. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,10 +21,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs index 6d5c953cbd5..484a90ab056 100644 --- a/library/std/src/sys/pal/windows/args/tests.rs +++ b/library/std/src/sys/pal/windows/args/tests.rs @@ -47,10 +47,10 @@ fn whitespace_behavior() { fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); - chk(r#"EXE "this is """all""" in the same argument""#, &[ - "EXE", - r#"this is "all" in the same argument"#, - ]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", r#"this is "all" in the same argument"#], + ); chk(r#"EXE "a"""#, &["EXE", r#"a""#]); chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index b3659351b8c..bdb55643bb1 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -296,6 +296,10 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { let path = maybe_verbatim(path)?; + Self::open_native(&path, opts) + } + + fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -328,9 +332,6 @@ impl File { mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32, ); if result == 0 { - if api::get_last_error().code != 0 { - panic!("FILE_ALLOCATION_INFO failed!!!"); - } let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; let result = c::SetFileInformationByHandle( handle.as_raw_handle(), @@ -1229,8 +1230,26 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> { pub fn unlink(p: &Path) -> io::Result<()> { let p_u16s = maybe_verbatim(p)?; - cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; - Ok(()) + if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { + let err = api::get_last_error(); + // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove + // the file while ignoring the readonly attribute. + // This is accomplished by calling the `posix_delete` function on an open file handle. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); + if let Ok(f) = File::open_native(&p_u16s, &opts) { + if f.posix_delete().is_ok() { + return Ok(()); + } + } + } + // return the original error if any of the above fails. + Err(io::Error::from_raw_os_error(err.code as i32)) + } else { + Ok(()) + } } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb5493..1eca346b76c 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -21,8 +21,6 @@ pub mod fs; #[cfg(not(target_vendor = "win7"))] pub mod futex; pub mod handle; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 1bcc5fa6b20..9a1eaf42fd9 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -158,7 +158,7 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; use crate::sys::fs::symlink; - use crate::sys_common::io::test::tmpdir; + use crate::test_helpers::tmpdir; let env_paths = || env::var_os("PATH"); diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd068560..1bd0e67f371 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,9 +5,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea05772029..054c867f90d 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -16,10 +16,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs index dd218c8894c..5f1d06dd1d7 100644 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -54,7 +54,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 24a94ec7828..1fa4e80d678 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -5,12 +5,12 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; - } else if #[cfg(any( - target_os = "uefi", - target_os = "solid_asp3", - ))] { + } else if #[cfg(target_os = "solid_asp3")] { mod unsupported_backslash; pub use unsupported_backslash::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs new file mode 100644 index 00000000000..a3f4a3bfe1b --- /dev/null +++ b/library/std/src/sys/path/uefi.rs @@ -0,0 +1,105 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::{helpers, unsupported_err}; + +const FORWARD_SLASH: u8 = b'/'; +const COLON: u8 = b':'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +/// UEFI paths can be of 4 types: +/// +/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present. +/// It can be identified with `:`. +/// Eg: FS0:\abc\run.efi +/// +/// 2. Absolute Device Path: this is what we want +/// It can be identified with `/`. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +/// +/// 3: Relative root: path relative to the current volume. +/// It will start with `\`. +/// Eg: \abc\run.efi +/// +/// 4: Relative +/// Eg: run.efi +/// +/// The algorithm is mostly taken from edk2 UEFI shell implementation and is +/// somewhat simple. Check for the path type in order. +/// +/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device +/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root +/// path), can just be appended to the remaining path. +/// +/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol +/// form) and join it with the relative root path. We then recurse the function to resolve the Shell +/// Mapping if present. +/// +/// For Relative paths, we use the current working directory to construct +/// the new path and recurse the function to resolve the Shell mapping if present. +/// +/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the +/// normal UEFI APIs such as file, process, etc. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> { + // Absolute Shell Path + if path.as_os_str().as_encoded_bytes().contains(&COLON) { + let mut path_components = path.components(); + // Since path is not empty, it has at least one Component + let prefix = path_components.next().unwrap(); + + let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?; + let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?; + + // UEFI Shell does not seem to end device path with `/` + if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH { + dev_path_text.push("/"); + } + + let mut ans = PathBuf::from(dev_path_text); + ans.push(path_components); + + return Ok(ans); + } + + // Absolute Device Path + if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) { + return Ok(path.to_path_buf()); + } + + // cur_dir() always returns something + let cur_dir = crate::env::current_dir().unwrap(); + let mut path_components = path.components(); + + // Relative Root + if path_components.next().unwrap() == crate::path::Component::RootDir { + let mut ans = PathBuf::new(); + ans.push(cur_dir.components().next().unwrap()); + ans.push(path_components); + return absolute(&ans); + } + + absolute(&cur_dir.join(path)) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + let temp = path.as_os_str().as_encoded_bytes(); + temp.contains(&COLON) || temp.contains(&FORWARD_SLASH) +} diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs deleted file mode 100644 index 6f6f282d432..00000000000 --- a/library/std/src/sys_common/io.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; - -#[cfg(test)] -#[allow(dead_code)] // not used on emscripten and wasi -pub mod test { - use rand::RngCore; - - use crate::path::{Path, PathBuf}; - use crate::{env, fs, thread}; - - pub struct TempDir(PathBuf); - - impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { - let TempDir(ref p) = *self; - p.join(path) - } - - pub fn path(&self) -> &Path { - let TempDir(ref p) = *self; - p - } - } - - impl Drop for TempDir { - fn drop(&mut self) { - // Gee, seeing how we're testing the fs module I sure hope that we - // at least implement this correctly! - let TempDir(ref p) = *self; - let result = fs::remove_dir_all(p); - // Avoid panicking while panicking as this causes the process to - // immediately abort, without displaying test results. - if !thread::panicking() { - result.unwrap(); - } - } - } - - #[track_caller] // for `test_rng` - pub fn tmpdir() -> TempDir { - let p = env::temp_dir(); - let mut r = crate::test_helpers::test_rng(); - let ret = p.join(&format!("rust-{}", r.next_u32())); - fs::create_dir(&ret).unwrap(); - TempDir(ret) - } -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb..4dc67d26bd8 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,25 +21,10 @@ mod tests; pub mod fs; -pub mod io; pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index bc06eaa2b8f..b57c99a8452 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -356,32 +356,32 @@ fn wtf8buf_from_iterator() { fn f(values: &[u32]) -> Wtf8Buf { values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>() } - assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + f(&[0x61, 0xE9, 0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD7FF, 0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0x61, 0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + f(&[0xD83D, 0x20, 0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD7FF, 0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0x61, 0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }); } @@ -396,36 +396,36 @@ fn wtf8buf_extend() { string } - assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD7FF], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0x61], &[0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + e(&[0xD83D, 0x20], &[0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD7FF], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0x61], &[0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); } #[test] @@ -556,9 +556,10 @@ fn wtf8_encode_wide() { let mut string = Wtf8Buf::from_str("aé "); string.push(CodePoint::from_u32(0xD83D).unwrap()); string.push_char('💩'); - assert_eq!(string.encode_wide().collect::<Vec<_>>(), vec![ - 0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9 - ]); + assert_eq!( + string.encode_wide().collect::<Vec<_>>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] + ); } #[test] diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs new file mode 100644 index 00000000000..7c20f38c863 --- /dev/null +++ b/library/std/src/test_helpers.rs @@ -0,0 +1,65 @@ +use rand::{RngCore, SeedableRng}; + +use crate::hash::{BuildHasher, Hash, Hasher, RandomState}; +use crate::panic::Location; +use crate::path::{Path, PathBuf}; +use crate::{env, fs, thread}; + +/// Test-only replacement for `rand::thread_rng()`, which is unusable for +/// us, as we want to allow running stdlib tests on tier-3 targets which may +/// not have `getrandom` support. +/// +/// Does a bit of a song and dance to ensure that the seed is different on +/// each call (as some tests sadly rely on this), but doesn't try that hard. +/// +/// This is duplicated in the `core`, `alloc` test suites (as well as +/// `std`'s integration tests), but figuring out a mechanism to share these +/// seems far more painful than copy-pasting a 7 line function a couple +/// times, given that even under a perma-unstable feature, I don't think we +/// want to expose types from `rand` from `std`. +#[track_caller] +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + let mut hasher = RandomState::new().build_hasher(); + Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::<Vec<u8>>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + SeedableRng::from_seed(seed) +} + +pub struct TempDir(PathBuf); + +impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path(&self) -> &Path { + let TempDir(ref p) = *self; + p + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } + } +} + +#[track_caller] // for `test_rng` +pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = test_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) +} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 2313f4b5beb..ca04aa4ada4 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -2,12 +2,6 @@ #![unstable(feature = "thread_local_internals", issue = "none")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(test)] -mod dynamic_tests; - use crate::cell::{Cell, RefCell}; use crate::error::Error; use crate::fmt; @@ -230,6 +224,14 @@ impl fmt::Display for AccessError { #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl Error for AccessError {} +// This ensures the panicking code is outlined from `with` for `LocalKey`. +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[cold] +fn panic_access_error(err: AccessError) -> ! { + panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}") +} + impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[unstable( @@ -269,10 +271,10 @@ impl<T: 'static> LocalKey<T> { where F: FnOnce(&T) -> R, { - self.try_with(f).expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match self.try_with(f) { + Ok(r) => r, + Err(err) => panic_access_error(err), + } } /// Acquires a reference to the value in this TLS key. @@ -327,10 +329,10 @@ impl<T: 'static> LocalKey<T> { let mut init = Some(init); let reference = unsafe { - (self.inner)(Some(&mut init)).as_ref().expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match (self.inner)(Some(&mut init)).as_ref() { + Some(r) => r, + None => panic_access_error(AccessError), + } }; f(init, reference) diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 9f4f8a0d088..88b3e9e0ceb 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -31,9 +31,6 @@ #![stable(feature = "time", since = "1.3.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; #[stable(feature = "duration_checked_float", since = "1.66.0")] diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 7cf70c725e4..1e8e4cced6c 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -18,7 +18,7 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// Copied from std::sys_common::io +// Copied from std::test_helpers pub(crate) struct TempDir(PathBuf); impl TempDir { diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 44fe84c989f..e754cf8263b 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -1,163 +1,123 @@ use std::env::*; -use std::ffi::{OsStr, OsString}; - -use rand::distributions::{Alphanumeric, DistString}; +use std::path::Path; mod common; -use std::thread; - -use common::test_rng; - -#[track_caller] -fn make_rand_name() -> OsString { - let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); - let n = OsString::from(n); - assert!(var_os(&n).is_none()); - n -} - -fn eq(a: Option<OsString>, b: Option<&str>) { - assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); -} #[test] -fn test_set_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - eq(var_os(&n), Some("VALUE")); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] +fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); } #[test] -fn test_remove_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - remove_var(&n); - eq(var_os(&n), None); -} +fn test() { + assert!((!Path::new("test-path").is_absolute())); -#[test] -fn test_set_var_overwrite() { - let n = make_rand_name(); - set_var(&n, "1"); - set_var(&n, "2"); - eq(var_os(&n), Some("2")); - set_var(&n, ""); - eq(var_os(&n), Some("")); + #[cfg(not(target_env = "sgx"))] + current_dir().unwrap(); } #[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_var_big() { - let mut s = "".to_string(); - let mut i = 0; - while i < 100 { - s.push_str("aaaaaaaaaa"); - i += 1; +#[cfg(windows)] +fn split_paths_windows() { + use std::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::<Vec<_>>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() } - let n = make_rand_name(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); -} -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_env_set_get_huge() { - let n = make_rand_name(); - let s = "x".repeat(10000); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); - remove_var(&n); - eq(var_os(&n), None); + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); } #[test] -fn test_env_set_var() { - let n = make_rand_name(); +#[cfg(unix)] +fn split_paths_unix() { + use std::path::PathBuf; - let mut e = vars_os(); - set_var(&n, "VALUE"); - assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::<Vec<_>>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>() + } - assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); } #[test] -#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] -#[allow(deprecated)] -fn env_home_dir() { - use std::path::PathBuf; +#[cfg(unix)] +fn join_paths_unix() { + use std::ffi::OsStr; - fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> { - match var { - Ok(var) => Some(OsString::from(var)), - Err(VarError::NotUnicode(var)) => Some(var), - _ => None, - } + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) } - cfg_if::cfg_if! { - if #[cfg(unix)] { - let oldhome = var_to_os_string(var("HOME")); - - set_var("HOME", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - - remove_var("HOME"); - if cfg!(target_os = "android") { - assert!(home_dir().is_none()); - } else { - // When HOME is not set, some platforms return `None`, - // but others return `Some` with a default. - // Just check that it is not "/home/MountainView". - assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - } - - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - } else if #[cfg(windows)] { - let oldhome = var_to_os_string(var("HOME")); - let olduserprofile = var_to_os_string(var("USERPROFILE")); - - remove_var("HOME"); - remove_var("USERPROFILE"); - - assert!(home_dir().is_some()); - - set_var("HOME", "/home/PaloAlto"); - assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + assert!(test_eq(&[], "")); + assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); + assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); + assert!(join_paths(["/te:st"].iter().cloned()).is_err()); +} - set_var("USERPROFILE", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); +#[test] +#[cfg(windows)] +fn join_paths_windows() { + use std::ffi::OsStr; - remove_var("HOME"); + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + assert!(test_eq(&[], "")); + assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); + assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); + assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); + assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); +} - set_var("USERPROFILE", ""); - assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); +#[test] +fn args_debug() { + assert_eq!( + format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()), + format!("{:?}", args()) + ); +} - remove_var("USERPROFILE"); +#[test] +fn args_os_debug() { + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()), + format!("{:?}", args_os()) + ); +} - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } - } - } +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()), + format!("{:?}", vars()) + ); } -#[test] // miri shouldn't detect any data race in this fn -#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] -fn test_env_get_set_multithreaded() { - let getter = thread::spawn(|| { - for _ in 0..100 { - let _ = var_os("foo"); - } - }); - - let setter = thread::spawn(|| { - for _ in 0..100 { - set_var("foo", "bar"); - } - }); - - let _ = getter.join(); - let _ = setter.join(); +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()), + format!("{:?}", vars_os()) + ); } diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs new file mode 100644 index 00000000000..60747447350 --- /dev/null +++ b/library/std/tests/env_modify.rs @@ -0,0 +1,166 @@ +// These tests are in a separate integration test as they modify the environment, +// and would otherwise cause some other tests to fail. + +use std::env::*; +use std::ffi::{OsStr, OsString}; + +use rand::distributions::{Alphanumeric, DistString}; + +mod common; +use std::thread; + +use common::test_rng; + +#[track_caller] +fn make_rand_name() -> OsString { + let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); + let n = OsString::from(n); + assert!(var_os(&n).is_none()); + n +} + +fn eq(a: Option<OsString>, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); +} + +#[test] +fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var_os(&n), Some("VALUE")); +} + +#[test] +fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_set_var_overwrite() { + let n = make_rand_name(); + set_var(&n, "1"); + set_var(&n, "2"); + eq(var_os(&n), Some("2")); + set_var(&n, ""); + eq(var_os(&n), Some("")); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = "x".repeat(10000); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars_os(); + set_var(&n, "VALUE"); + assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + + assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); +} + +#[test] +#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] +#[allow(deprecated)] +fn env_home_dir() { + use std::path::PathBuf; + + fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> { + match var { + Ok(var) => Some(OsString::from(var)), + Err(VarError::NotUnicode(var)) => Some(var), + _ => None, + } + } + + cfg_if::cfg_if! { + if #[cfg(unix)] { + let oldhome = var_to_os_string(var("HOME")); + + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + if cfg!(target_os = "android") { + assert!(home_dir().is_none()); + } else { + // When HOME is not set, some platforms return `None`, + // but others return `Some` with a default. + // Just check that it is not "/home/MountainView". + assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + } + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + } else if #[cfg(windows)] { + let oldhome = var_to_os_string(var("HOME")); + let olduserprofile = var_to_os_string(var("USERPROFILE")); + + remove_var("HOME"); + remove_var("USERPROFILE"); + + assert!(home_dir().is_some()); + + set_var("HOME", "/home/PaloAlto"); + assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + + set_var("USERPROFILE", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + set_var("USERPROFILE", ""); + assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); + + remove_var("USERPROFILE"); + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } + } + } +} + +#[test] // miri shouldn't detect any data race in this fn +#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] +fn test_env_get_set_multithreaded() { + let getter = thread::spawn(|| { + for _ in 0..100 { + let _ = var_os("foo"); + } + }); + + let setter = thread::spawn(|| { + for _ in 0..100 { + set_var("foo", "bar"); + } + }); + + let _ = getter.join(); + let _ = setter.join(); +} diff --git a/library/std/src/error/tests.rs b/library/std/tests/error.rs index 88a9f33c079..8fd6eb3c020 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/tests/error.rs @@ -1,7 +1,8 @@ -use core::error::Request; +#![feature(error_generic_member_access, error_reporter)] -use super::Error; -use crate::fmt; +use std::backtrace::Backtrace; +use std::error::{Error, Report, Request}; +use std::fmt; #[derive(Debug, PartialEq)] struct A; @@ -38,9 +39,6 @@ fn downcasting() { } } -use crate::backtrace::Backtrace; -use crate::error::Report; - #[derive(Debug)] struct SuperError { source: SuperErrorSideKick, diff --git a/library/std/src/f128/tests.rs b/library/std/tests/floats/f128.rs index cbcf9f96239..d0e8b157e6b 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/tests/floats/f128.rs @@ -1,11 +1,11 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f128)] -use crate::f128::consts; -use crate::num::FpCategory as Fp; +use std::f128::consts; +use std::num::FpCategory as Fp; #[cfg(reliable_f128_math)] -use crate::ops::Rem; -use crate::ops::{Add, Div, Mul, Sub}; +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; // Note these tolerances make sense around zero, but not for more extreme exponents. @@ -762,8 +762,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f128 = consts::PI; let frac_pi_2: f128 = consts::FRAC_PI_2; let frac_pi_3: f128 = consts::FRAC_PI_3; diff --git a/library/std/src/f16/tests.rs b/library/std/tests/floats/f16.rs index 684ee3f3855..5180f3d40f3 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/tests/floats/f16.rs @@ -1,8 +1,8 @@ // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy #![cfg(reliable_f16)] -use crate::f16::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f16::consts; +use std::num::FpCategory as Fp; /// Tolerance for results on the order of 10.0e-2 #[allow(unused)] @@ -54,7 +54,7 @@ macro_rules! assert_f16_biteq { #[test] fn test_num_f16() { - test_num(10f16, 2f16); + crate::test_num(10f16, 2f16); } #[test] @@ -734,7 +734,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available - use super::consts; let pi: f16 = consts::PI; let frac_pi_2: f16 = consts::FRAC_PI_2; diff --git a/library/std/src/f32/tests.rs b/library/std/tests/floats/f32.rs index 99cfcfb231d..bf7641986ad 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/tests/floats/f32.rs @@ -1,5 +1,5 @@ -use crate::f32::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f32::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u32 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f32_biteq { #[test] fn test_num_f32() { - test_num(10f32, 2f32); + crate::test_num(10f32, 2f32); } #[test] @@ -700,8 +700,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; - let pi: f32 = consts::PI; let frac_pi_2: f32 = consts::FRAC_PI_2; let frac_pi_3: f32 = consts::FRAC_PI_3; diff --git a/library/std/src/f64/tests.rs b/library/std/tests/floats/f64.rs index 3fac2efe0d7..cbbfcd15efd 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/tests/floats/f64.rs @@ -1,5 +1,5 @@ -use crate::f64::consts; -use crate::num::{FpCategory as Fp, *}; +use std::f64::consts; +use std::num::FpCategory as Fp; /// Smallest number const TINY_BITS: u64 = 0x1; @@ -35,7 +35,7 @@ macro_rules! assert_f64_biteq { #[test] fn test_num_f64() { - test_num(10f64, 2f64); + crate::test_num(10f64, 2f64); } #[test] @@ -112,7 +112,6 @@ fn test_neg_zero() { assert_eq!(Fp::Zero, neg_zero.classify()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_one() { let one: f64 = 1.0f64; @@ -165,7 +164,6 @@ fn test_is_finite() { assert!((-109.2f64).is_finite()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_is_normal() { let nan: f64 = f64::NAN; @@ -183,7 +181,6 @@ fn test_is_normal() { assert!(!1e-308f64.is_normal()); } -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 #[test] fn test_classify() { let nan: f64 = f64::NAN; @@ -683,7 +680,6 @@ fn test_ln_gamma() { #[test] fn test_real_consts() { - use super::consts; let pi: f64 = consts::PI; let frac_pi_2: f64 = consts::FRAC_PI_2; let frac_pi_3: f64 = consts::FRAC_PI_3; diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs new file mode 100644 index 00000000000..ad82f1a44e7 --- /dev/null +++ b/library/std/tests/floats/lib.rs @@ -0,0 +1,42 @@ +#![feature(f16, f128, float_gamma, float_minimum_maximum)] + +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num<T>(ten: T, two: T) +where + T: PartialEq + + Add<Output = T> + + Sub<Output = T> + + Mul<Output = T> + + Div<Output = T> + + Rem<Output = T> + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/std/tests/istr.rs b/library/std/tests/istr.rs index 9a127ae803e..e481872977a 100644 --- a/library/std/tests/istr.rs +++ b/library/std/tests/istr.rs @@ -5,7 +5,7 @@ fn test_stack_assign() { let t: String = "a".to_string(); assert_eq!(s, t); let u: String = "b".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] @@ -19,7 +19,7 @@ fn test_heap_assign() { let t: String = "a big ol' string".to_string(); assert_eq!(s, t); let u: String = "a bad ol' string".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] diff --git a/library/std/src/num/tests.rs b/library/std/tests/num.rs index df0df3f23f7..a7400f1c02d 100644 --- a/library/std/src/num/tests.rs +++ b/library/std/tests/num.rs @@ -1,4 +1,4 @@ -use crate::ops::Mul; +use std::ops::Mul; #[test] fn test_saturating_add_uint() { @@ -190,8 +190,8 @@ fn test_uint_to_str_overflow() { assert_eq!(u64_val.to_string(), "0"); } -fn from_str<T: crate::str::FromStr>(t: &str) -> Option<T> { - crate::str::FromStr::from_str(t).ok() +fn from_str<T: std::str::FromStr>(t: &str) -> Option<T> { + std::str::FromStr::from_str(t).ok() } #[test] diff --git a/library/std/src/panic/tests.rs b/library/std/tests/panic.rs index b37d74011cc..f13b931dd22 100644 --- a/library/std/src/panic/tests.rs +++ b/library/std/tests/panic.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] -use crate::cell::RefCell; -use crate::panic::{AssertUnwindSafe, UnwindSafe}; -use crate::rc::Rc; -use crate::sync::{Arc, Mutex, RwLock}; +use std::cell::RefCell; +use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::rc::Rc; +use std::sync::{Arc, Mutex, RwLock}; struct Foo { a: i32, diff --git a/library/std/src/path/tests.rs b/library/std/tests/path.rs index 3f96ac4672a..978402b6fda 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/tests/path.rs @@ -1,10 +1,19 @@ -use core::hint::black_box; - -use super::*; -use crate::collections::{BTreeSet, HashSet}; -use crate::hash::DefaultHasher; -use crate::mem::MaybeUninit; -use crate::ptr; +#![feature( + clone_to_uninit, + path_add_extension, + path_file_prefix, + maybe_uninit_slice, + os_string_pathbuf_leak +)] + +use std::clone::CloneToUninit; +use std::ffi::OsStr; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::mem::MaybeUninit; +use std::path::*; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -110,7 +119,7 @@ macro_rules! t ( #[test] fn into() { - use crate::borrow::Cow; + use std::borrow::Cow; let static_path = Path::new("/home/foo"); let static_cow_path: Cow<'static, Path> = static_path.into(); @@ -1525,7 +1534,7 @@ pub fn test_with_added_extension() { #[test] fn test_eq_receivers() { - use crate::borrow::Cow; + use std::borrow::Cow; let borrowed: &Path = Path::new("foo/bar"); let mut owned: PathBuf = PathBuf::new(); @@ -1550,7 +1559,7 @@ fn test_eq_receivers() { #[test] pub fn test_compare() { - use crate::hash::{DefaultHasher, Hash, Hasher}; + use std::hash::{DefaultHasher, Hash, Hasher}; fn hash<T: Hash>(t: T) -> u64 { let mut s = DefaultHasher::new(); @@ -1867,12 +1876,12 @@ fn test_ord() { #[test] #[cfg(any(unix, target_os = "wasi"))] fn test_unix_absolute() { - use crate::path::absolute; + use std::path::absolute; assert!(absolute("").is_err()); let relative = "a/b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1888,7 +1897,7 @@ fn test_unix_absolute() { ); // Test leading `.` and `..` components - let curdir = crate::env::current_dir().unwrap(); + let curdir = std::env::current_dir().unwrap(); assert_eq!(absolute("./a").unwrap().as_os_str(), curdir.join("a").as_os_str()); assert_eq!(absolute("../a").unwrap().as_os_str(), curdir.join("../a").as_os_str()); // return /pwd/../a } @@ -1896,12 +1905,12 @@ fn test_unix_absolute() { #[test] #[cfg(windows)] fn test_windows_absolute() { - use crate::path::absolute; + use std::path::absolute; // An empty path is an error. assert!(absolute("").is_err()); let relative = r"a\b"; - let mut expected = crate::env::current_dir().unwrap(); + let mut expected = std::env::current_dir().unwrap(); expected.push(relative); assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); @@ -1953,116 +1962,6 @@ fn test_extension_path_sep_alternate() { assert_eq!(path, Path::new("path/to/file.d\\test")); } -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { - let prefix = "my/home"; - let mut paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - paths.sort(); - - b.iter(|| { - black_box(paths.as_mut_slice()).sort_unstable(); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { - let prefix = "my/home"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(black_box(paths[500].as_path())) - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset_miss(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - let probe = PathBuf::from(prefix).join("other"); - - b.iter(|| set.remove(black_box(probe.as_path()))); -} - -#[bench] -fn bench_hash_path_short(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = Path::new("explorer.exe"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - -#[bench] -fn bench_hash_path_long(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = - Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - #[test] fn clone_to_uninit() { let a = Path::new("hello.txt"); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index df946cdcf2b..00d99a578d5 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,7 +1,7 @@ #![feature(anonymous_pipe)] fn main() { - #[cfg(all(not(miri), any(unix, windows)))] + #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] { use std::io::{Read, pipe}; use std::{env, process}; diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 3e72e371ade..43b45cb2d2b 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -5,7 +5,8 @@ use std::{env, fs, process, str}; mod common; #[test] -#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi +// Process spawning not supported by Miri, Emscripten and wasi +#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/seq-compare.rs b/library/std/tests/seq-compare.rs index 221f1c7cabd..ec39c5b603c 100644 --- a/library/std/tests/seq-compare.rs +++ b/library/std/tests/seq-compare.rs @@ -1,15 +1,15 @@ #[test] fn seq_compare() { - assert!(("hello".to_string() < "hellr".to_string())); - assert!(("hello ".to_string() > "hello".to_string())); - assert!(("hello".to_string() != "there".to_string())); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); - assert!((vec![1, 2, 3] < vec![1, 2, 3, 4])); - assert!((vec![1, 2, 4, 4] > vec![1, 2, 3, 4])); - assert!((vec![1, 2, 3, 4] < vec![1, 2, 4, 4])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3, 3])); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); + assert!("hello".to_string() < "hellr".to_string()); + assert!("hello ".to_string() > "hello".to_string()); + assert!("hello".to_string() != "there".to_string()); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); + assert!(vec![1, 2, 3] < vec![1, 2, 3, 4]); + assert!(vec![1, 2, 4, 4] > vec![1, 2, 3, 4]); + assert!(vec![1, 2, 3, 4] < vec![1, 2, 4, 4]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); assert_eq!(vec![1, 2, 3], vec![1, 2, 3]); - assert!((vec![1, 2, 3] != vec![1, 1, 3])); + assert!(vec![1, 2, 3] != vec![1, 1, 3]); } diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/tests/sync/barrier.rs index 0fbcd998812..8aefff9d507 100644 --- a/library/std/src/sync/barrier/tests.rs +++ b/library/std/tests/sync/barrier.rs @@ -1,6 +1,6 @@ -use crate::sync::mpsc::{TryRecvError, channel}; -use crate::sync::{Arc, Barrier}; -use crate::thread; +use std::sync::mpsc::{TryRecvError, channel}; +use std::sync::{Arc, Barrier}; +use std::thread; #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads diff --git a/library/std/src/sync/poison/condvar/tests.rs b/library/std/tests/sync/condvar.rs index f9e9066bc92..834de6bb1c2 100644 --- a/library/std/src/sync/poison/condvar/tests.rs +++ b/library/std/tests/sync/condvar.rs @@ -1,8 +1,8 @@ -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread; -use crate::time::Duration; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::time::Duration; #[test] fn smoke() { diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/tests/sync/lazy_lock.rs index 7d7dde54349..6c14b79f2ce 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/tests/sync/lazy_lock.rs @@ -1,8 +1,8 @@ -use crate::cell::LazyCell; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{LazyLock, Mutex, OnceLock}; -use crate::{panic, thread}; +use std::cell::LazyCell; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::{LazyLock, Mutex, OnceLock}; +use std::{panic, thread}; fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -149,7 +149,7 @@ fn is_sync_send() { #[should_panic = "has previously been poisoned"] fn lazy_force_mut_panic() { let mut lazy = LazyLock::<String>::new(|| panic!()); - crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { + panic::catch_unwind(panic::AssertUnwindSafe(|| { let _ = LazyLock::force_mut(&mut lazy); })) .unwrap_err(); diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs new file mode 100644 index 00000000000..51190f0894f --- /dev/null +++ b/library/std/tests/sync/lib.rs @@ -0,0 +1,31 @@ +#![feature(lazy_get)] +#![feature(mapped_lock_guards)] +#![feature(mpmc_channel)] +#![feature(once_cell_try)] +#![feature(lock_value_accessors)] +#![feature(reentrant_lock)] +#![feature(rwlock_downgrade)] +#![feature(std_internals)] +#![allow(internal_features)] + +mod barrier; +mod condvar; +mod lazy_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpmc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc_sync; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mutex; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod once; +mod once_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod reentrant_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod rwlock; + +#[path = "../common/mod.rs"] +mod common; diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/tests/sync/mpmc.rs index ab14050df6c..81b92297f76 100644 --- a/library/std/src/sync/mpmc/tests.rs +++ b/library/std/tests/sync/mpmc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpmc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/tests/sync/mpsc.rs index 13892fa0d18..1d8edfde44b 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/tests/sync/mpsc.rs @@ -1,5 +1,6 @@ -use super::*; -use crate::{env, thread}; +use std::sync::mpsc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/tests/sync/mpsc_sync.rs index 49b65c8efe6..a7f326d201b 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/tests/sync/mpsc_sync.rs @@ -1,7 +1,8 @@ -use super::*; -use crate::rc::Rc; -use crate::sync::mpmc::SendTimeoutError; -use crate::{env, thread}; +use std::rc::Rc; +use std::sync::mpmc::SendTimeoutError; +use std::sync::mpsc::*; +use std::time::Duration; +use std::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/poison/mutex/tests.rs b/library/std/tests/sync/mutex.rs index 395c8aada08..74c62720107 100644 --- a/library/std/src/sync/poison/mutex/tests.rs +++ b/library/std/tests/sync/mutex.rs @@ -1,10 +1,10 @@ -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; -use crate::{hint, mem, thread}; +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::{hint, mem, thread}; struct Packet<T>(Arc<(Mutex<T>, Condvar)>); diff --git a/library/std/src/sync/poison/once/tests.rs b/library/std/tests/sync/once.rs index ce96468aeb6..a3ffc73fe06 100644 --- a/library/std/src/sync/poison/once/tests.rs +++ b/library/std/tests/sync/once.rs @@ -1,9 +1,9 @@ -use super::Once; -use crate::sync::atomic::AtomicBool; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::mpsc::channel; -use crate::time::Duration; -use crate::{panic, thread}; +use std::sync::Once; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::mpsc::channel; +use std::time::Duration; +use std::{panic, thread}; #[test] fn smoke_once() { diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/tests/sync/once_lock.rs index 5113d436c3c..ac9aaa8892e 100644 --- a/library/std/src/sync/once_lock/tests.rs +++ b/library/std/tests/sync/once_lock.rs @@ -1,8 +1,8 @@ -use crate::sync::OnceLock; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::mpsc::channel; -use crate::{panic, thread}; +use std::sync::OnceLock; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::mpsc::channel; +use std::{panic, thread}; fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() @@ -34,15 +34,6 @@ fn sync_once_cell_get_mut() { } #[test] -fn sync_once_cell_get_unchecked() { - let c = OnceLock::new(); - c.set(92).unwrap(); - unsafe { - assert_eq!(c.get_unchecked(), &92); - } -} - -#[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_once_cell_drop() { static DROP_CNT: AtomicUsize = AtomicUsize::new(0); @@ -88,7 +79,6 @@ fn get_or_try_init() { let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); assert!(res.is_err()); - assert!(!cell.is_initialized()); assert!(cell.get().is_none()); assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); @@ -174,7 +164,7 @@ fn sync_once_cell_does_not_leak_partially_constructed_boxes() { break; } #[cfg(target_env = "sgx")] - crate::thread::yield_now(); + std::thread::yield_now(); } }); } diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/tests/sync/reentrant_lock.rs index aeef0289d28..2b7b87e3623 100644 --- a/library/std/src/sync/reentrant_lock/tests.rs +++ b/library/std/tests/sync/reentrant_lock.rs @@ -1,7 +1,6 @@ -use super::ReentrantLock; -use crate::cell::RefCell; -use crate::sync::Arc; -use crate::thread; +use std::cell::RefCell; +use std::sync::{Arc, ReentrantLock}; +use std::thread; #[test] fn smoke() { diff --git a/library/std/src/sync/poison/rwlock/tests.rs b/library/std/tests/sync/rwlock.rs index 057c2f1a5d7..bd4bc7a14bc 100644 --- a/library/std/src/sync/poison/rwlock/tests.rs +++ b/library/std/tests/sync/rwlock.rs @@ -1,15 +1,15 @@ -use rand::Rng; - -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{ +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use crate::{hint, mem, thread}; +use std::{hint, mem, thread}; + +use rand::Rng; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); @@ -57,7 +57,7 @@ fn frob() { let tx = tx.clone(); let r = r.clone(); thread::spawn(move || { - let mut rng = crate::test_helpers::test_rng(); + let mut rng = crate::common::test_rng(); for _ in 0..M { if rng.gen_bool(1.0 / (N as f64)) { drop(r.write().unwrap()); @@ -704,7 +704,7 @@ fn test_downgrade_atomic() { // Wait for a good amount of time so that evil threads go to sleep. // Note: this is not strictly necessary... - let eternity = crate::time::Duration::from_millis(42); + let eternity = std::time::Duration::from_millis(42); thread::sleep(eternity); // Once everyone is asleep, set the value to `NEW_VALUE`. diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/tests/thread_local/dynamic_tests.rs index dd180041648..454462b3925 100644 --- a/library/std/src/thread/local/dynamic_tests.rs +++ b/library/std/tests/thread_local/dynamic_tests.rs @@ -1,6 +1,6 @@ -use crate::cell::RefCell; -use crate::collections::HashMap; -use crate::thread_local; +use std::cell::RefCell; +use std::collections::HashMap; +use std::thread_local; #[test] fn smoke() { diff --git a/library/std/tests/thread_local/lib.rs b/library/std/tests/thread_local/lib.rs new file mode 100644 index 00000000000..c5291435425 --- /dev/null +++ b/library/std/tests/thread_local/lib.rs @@ -0,0 +1,4 @@ +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod tests; + +mod dynamic_tests; diff --git a/library/std/src/thread/local/tests.rs b/library/std/tests/thread_local/tests.rs index 9d4f52a0921..aa020c2559c 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/tests/thread_local/tests.rs @@ -1,8 +1,8 @@ -use crate::cell::{Cell, UnsafeCell}; -use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, Builder, LocalKey}; -use crate::thread_local; +use std::cell::{Cell, UnsafeCell}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread::{self, Builder, LocalKey}; +use std::thread_local; #[derive(Clone, Default)] struct Signal(Arc<(Mutex<bool>, Condvar)>); diff --git a/library/std/src/time/tests.rs b/library/std/tests/time.rs index e88f2d5e806..40709eae37c 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/tests/time.rs @@ -1,9 +1,7 @@ -use core::fmt::Debug; +#![feature(duration_constants)] -#[cfg(not(target_arch = "wasm32"))] -use test::{Bencher, black_box}; - -use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::fmt::Debug; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ @@ -29,10 +27,10 @@ fn instant_monotonic() { #[test] #[cfg(not(target_arch = "wasm32"))] -fn instant_monotonic_concurrent() -> crate::thread::Result<()> { +fn instant_monotonic_concurrent() -> std::thread::Result<()> { let threads: Vec<_> = (0..8) .map(|_| { - crate::thread::spawn(|| { + std::thread::spawn(|| { let mut old = Instant::now(); let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; for _ in 0..count { @@ -229,46 +227,3 @@ fn big_math() { check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); } - -macro_rules! bench_instant_threaded { - ($bench_name:ident, $thread_count:expr) => { - #[bench] - #[cfg(not(target_arch = "wasm32"))] - fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { - use crate::sync::Arc; - use crate::sync::atomic::{AtomicBool, Ordering}; - - let running = Arc::new(AtomicBool::new(true)); - - let threads: Vec<_> = (0..$thread_count) - .map(|_| { - let flag = Arc::clone(&running); - crate::thread::spawn(move || { - while flag.load(Ordering::Relaxed) { - black_box(Instant::now()); - } - }) - }) - .collect(); - - b.iter(|| { - let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); - }); - - running.store(false, Ordering::Relaxed); - - for t in threads { - t.join()?; - } - Ok(()) - } - }; -} - -bench_instant_threaded!(instant_contention_01_threads, 0); -bench_instant_threaded!(instant_contention_02_threads, 1); -bench_instant_threaded!(instant_contention_04_threads, 3); -bench_instant_threaded!(instant_contention_08_threads, 7); -bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs new file mode 100644 index 00000000000..1c3ce4d710c --- /dev/null +++ b/library/std/tests/win_delete_self.rs @@ -0,0 +1,8 @@ +#![cfg(windows)] + +/// Attempting to delete a running binary should return an error on Windows. +#[test] +fn win_delete_self() { + let path = std::env::current_exe().unwrap(); + assert!(std::fs::remove_file(path).is_err()); +} |
