diff options
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/f32.rs | 4 | ||||
| -rw-r--r-- | library/std/src/f64.rs | 4 | ||||
| -rw-r--r-- | library/std/src/fs.rs | 7 | ||||
| -rw-r--r-- | library/std/src/fs/tests.rs | 18 | ||||
| -rw-r--r-- | library/std/src/io/cursor.rs | 26 | ||||
| -rw-r--r-- | library/std/src/io/mod.rs | 2 | ||||
| -rw-r--r-- | library/std/src/io/stdio.rs | 12 | ||||
| -rw-r--r-- | library/std/src/lib.rs | 1 | ||||
| -rw-r--r-- | library/std/src/os/windows/process.rs | 13 | ||||
| -rw-r--r-- | library/std/src/panic.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/unix/android.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/unix/fs.rs | 50 | ||||
| -rw-r--r-- | library/std/src/sys/unix/kernel_copy.rs | 1 | ||||
| -rw-r--r-- | library/std/src/sys/unix/os.rs | 3 | ||||
| -rw-r--r-- | library/std/src/sys/unix/process/process_unix.rs | 8 | ||||
| -rw-r--r-- | library/std/src/sys/unix/rand.rs | 4 | ||||
| -rw-r--r-- | library/std/src/sys/unix/thread.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/unix/weak.rs | 13 | ||||
| -rw-r--r-- | library/std/src/sys/windows/process.rs | 77 | ||||
| -rw-r--r-- | library/std/src/sys/windows/process/tests.rs | 28 | ||||
| -rw-r--r-- | library/std/src/thread/mod.rs | 30 |
21 files changed, 220 insertions, 89 deletions
diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 21bd79611a5..e0cc6ad1d42 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -324,18 +324,20 @@ impl f32 { /// Returns the square root of a number. /// - /// Returns NaN if `self` is a negative number. + /// Returns NaN if `self` is a negative number other than `-0.0`. /// /// # Examples /// /// ``` /// let positive = 4.0_f32; /// let negative = -4.0_f32; + /// let negative_zero = -0.0_f32; /// /// let abs_difference = (positive.sqrt() - 2.0).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 8c8cf73741b..7ed65b7dafe 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -324,18 +324,20 @@ impl f64 { /// Returns the square root of a number. /// - /// Returns NaN if `self` is a negative number. + /// Returns NaN if `self` is a negative number other than `-0.0`. /// /// # Examples /// /// ``` /// let positive = 4.0_f64; /// let negative = -4.0_f64; + /// let negative_zero = -0.0_f64; /// /// let abs_difference = (positive.sqrt() - 2.0).abs(); /// /// assert!(abs_difference < 1e-10); /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index a9ce814e2ec..fb928ea5598 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1736,8 +1736,11 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `linkat` function with no flags -/// on Unix and the `CreateHardLink` function on Windows. +/// This function currently corresponds the `CreateHardLink` function on Windows. +/// On most Unix systems, it corresponds to the `linkat` function with no flags. +/// On Android, VxWorks, and Redox, it instead corresponds to the `link` function. +/// On MacOS, it uses the `linkat` function if it is available, but on very old +/// systems where `linkat` is not available, `link` is selected at runtime instead. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 127b7bf34a3..080b4b5d87f 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -19,6 +19,10 @@ use crate::os::unix::fs::symlink as symlink_junction; use crate::os::windows::fs::{symlink_dir, symlink_file}; #[cfg(windows)] use crate::sys::fs::symlink_junction; +#[cfg(target_os = "macos")] +use crate::sys::weak::weak; +#[cfg(target_os = "macos")] +use libc::{c_char, c_int}; macro_rules! check { ($e:expr) => { @@ -79,6 +83,17 @@ pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { } } +#[cfg(target_os = "macos")] +fn able_to_not_follow_symlinks_while_hard_linking() -> bool { + weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); + linkat.get().is_some() +} + +#[cfg(not(target_os = "macos"))] +fn able_to_not_follow_symlinks_while_hard_linking() -> bool { + return true; +} + #[test] fn file_test_io_smoke_test() { let message = "it's alright. have a good time"; @@ -1347,6 +1362,9 @@ fn symlink_hard_link() { if !got_symlink_permission(&tmpdir) { return; }; + if !able_to_not_follow_symlinks_while_hard_linking() { + return; + } // Create "file", a file. check!(fs::File::create(tmpdir.join("file"))); diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 04f13cdeb88..ae0cea985d7 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -209,32 +209,6 @@ impl<T> Cursor<T> where T: AsRef<[u8]>, { - /// Returns the remaining length. - /// - /// # Examples - /// - /// ``` - /// #![feature(cursor_remaining)] - /// use std::io::Cursor; - /// - /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); - /// - /// assert_eq!(buff.remaining(), 5); - /// - /// buff.set_position(2); - /// assert_eq!(buff.remaining(), 3); - /// - /// buff.set_position(4); - /// assert_eq!(buff.remaining(), 1); - /// - /// buff.set_position(6); - /// assert_eq!(buff.remaining(), 0); - /// ``` - #[unstable(feature = "cursor_remaining", issue = "86369")] - pub fn remaining(&self) -> u64 { - (self.inner.as_ref().len() as u64).checked_sub(self.pos).unwrap_or(0) - } - /// Returns the remaining slice. /// /// # Examples diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 9daeee711ad..ad8975c03f1 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -277,7 +277,7 @@ pub use self::error::{Error, ErrorKind, Result}; pub use self::stdio::set_output_capture; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; -#[unstable(feature = "stdio_locked", issue = "none")] +#[unstable(feature = "stdio_locked", issue = "86845")] pub use self::stdio::{stderr_locked, stdin_locked, stdout_locked}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 293f0e31ce0..206687e38fb 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -347,7 +347,7 @@ pub fn stdin() -> Stdin { /// Ok(()) /// } /// ``` -#[unstable(feature = "stdio_locked", issue = "none")] +#[unstable(feature = "stdio_locked", issue = "86845")] pub fn stdin_locked() -> StdinLock<'static> { stdin().into_locked() } @@ -442,7 +442,7 @@ impl Stdin { /// Ok(()) /// } /// ``` - #[unstable(feature = "stdio_locked", issue = "none")] + #[unstable(feature = "stdio_locked", issue = "86845")] pub fn into_locked(self) -> StdinLock<'static> { self.lock_any() } @@ -668,7 +668,7 @@ pub fn stdout() -> Stdout { /// Ok(()) /// } /// ``` -#[unstable(feature = "stdio_locked", issue = "none")] +#[unstable(feature = "stdio_locked", issue = "86845")] pub fn stdout_locked() -> StdoutLock<'static> { stdout().into_locked() } @@ -745,7 +745,7 @@ impl Stdout { /// Ok(()) /// } /// ``` - #[unstable(feature = "stdio_locked", issue = "none")] + #[unstable(feature = "stdio_locked", issue = "86845")] pub fn into_locked(self) -> StdoutLock<'static> { self.lock_any() } @@ -945,7 +945,7 @@ pub fn stderr() -> Stderr { /// Ok(()) /// } /// ``` -#[unstable(feature = "stdio_locked", issue = "none")] +#[unstable(feature = "stdio_locked", issue = "86845")] pub fn stderr_locked() -> StderrLock<'static> { stderr().into_locked() } @@ -1005,7 +1005,7 @@ impl Stderr { /// Ok(()) /// } /// ``` - #[unstable(feature = "stdio_locked", issue = "none")] + #[unstable(feature = "stdio_locked", issue = "86845")] pub fn into_locked(self) -> StderrLock<'static> { self.lock_any() } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6f389f000af..8c120f4af28 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -278,6 +278,7 @@ #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(integer_atomics)] +#![feature(int_log)] #![feature(into_future)] #![feature(intra_doc_pointers)] #![feature(iter_zip)] diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 67756b15531..9e7ccd015b6 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -2,6 +2,7 @@ #![stable(feature = "process_extensions", since = "1.2.0")] +use crate::ffi::OsStr; use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::process; use crate::sealed::Sealed; @@ -125,6 +126,13 @@ pub trait CommandExt: Sealed { /// [2]: <https://msdn.microsoft.com/en-us/library/17w5ykft.aspx> #[unstable(feature = "windows_process_extensions_force_quotes", issue = "82227")] fn force_quotes(&mut self, enabled: bool) -> &mut process::Command; + + /// Append literal text to the command line without any quoting or escaping. + /// + /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow + /// `CommandLineToArgvW` escaping rules. + #[unstable(feature = "windows_process_extensions_raw_arg", issue = "29494")] + fn raw_arg<S: AsRef<OsStr>>(&mut self, text_to_append_as_is: S) -> &mut process::Command; } #[stable(feature = "windows_process_extensions", since = "1.16.0")] @@ -138,4 +146,9 @@ impl CommandExt for process::Command { self.as_inner_mut().force_quotes(enabled); self } + + fn raw_arg<S: AsRef<OsStr>>(&mut self, raw_text: S) -> &mut process::Command { + self.as_inner_mut().raw_arg(raw_text.as_ref()); + self + } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index ee069eefd45..7bc987db881 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -133,7 +133,7 @@ pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! { /// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be /// implemented for any closed over variables passed to `catch_unwind`. #[stable(feature = "catch_unwind", since = "1.9.0")] -#[cfg_attr(not(test), lang = "unwind_safe")] +#[cfg_attr(not(test), rustc_diagnostic_item = "unwind_safe_trait")] #[rustc_on_unimplemented( message = "the type `{Self}` may not be safely transferred across an unwind boundary", label = "`{Self}` may not be safely transferred across an unwind boundary" @@ -149,7 +149,7 @@ pub auto trait UnwindSafe {} /// This is a "helper marker trait" used to provide impl blocks for the /// [`UnwindSafe`] trait, for more information see that documentation. #[stable(feature = "catch_unwind", since = "1.9.0")] -#[cfg_attr(not(test), lang = "ref_unwind_safe")] +#[cfg_attr(not(test), rustc_diagnostic_item = "ref_unwind_safe_trait")] #[rustc_on_unimplemented( message = "the type `{Self}` may contain interior mutability and a reference may not be safely \ transferrable across a catch_unwind boundary", diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs index cf6aa31b7cf..6a46525f682 100644 --- a/library/std/src/sys/unix/android.rs +++ b/library/std/src/sys/unix/android.rs @@ -21,7 +21,7 @@ use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; use libc::{ftruncate, pread, pwrite}; -use super::{cvt, cvt_r}; +use super::{cvt, cvt_r, weak::weak}; use crate::io; // The `log2` and `log2f` functions apparently appeared in android-18, or at diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index bec0d5898ac..5c8c94971c3 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -12,8 +12,23 @@ use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner}; +#[cfg(any( + all(target_os = "linux", target_env = "gnu"), + target_os = "macos", + target_os = "ios", +))] +use crate::sys::weak::syscall; +#[cfg(target_os = "macos")] +use crate::sys::weak::weak; + use libc::{c_int, mode_t}; +#[cfg(any( + target_os = "macos", + target_os = "ios", + all(target_os = "linux", target_env = "gnu") +))] +use libc::c_char; #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] use libc::dirfd; #[cfg(any(target_os = "linux", target_os = "emscripten"))] @@ -92,7 +107,7 @@ cfg_has_statx! {{ // Default `stat64` contains no creation time. unsafe fn try_statx( fd: c_int, - path: *const libc::c_char, + path: *const c_char, flags: i32, mask: u32, ) -> Option<io::Result<FileAttr>> { @@ -107,7 +122,7 @@ cfg_has_statx! {{ syscall! { fn statx( fd: c_int, - pathname: *const libc::c_char, + pathname: *const c_char, flags: c_int, mask: libc::c_uint, statxbuf: *mut libc::statx @@ -756,7 +771,7 @@ impl File { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( fd, - b"\0" as *const _ as *const libc::c_char, + b"\0" as *const _ as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, libc::STATX_ALL, ) } { @@ -1087,15 +1102,28 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { let link = cstr(link)?; cfg_if::cfg_if! { if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] { - // VxWorks, Redox, and old versions of Android lack `linkat`, so use - // `link` instead. POSIX leaves it implementation-defined whether - // `link` follows symlinks, so rely on the `symlink_hard_link` test - // in library/std/src/fs/tests.rs to check the behavior. + // VxWorks and Redox lack `linkat`, so use `link` instead. POSIX leaves + // it implementation-defined whether `link` follows symlinks, so rely on the + // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. + // Android has `linkat` on newer versions, but we happen to know `link` + // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + } else if #[cfg(target_os = "macos")] { + // On MacOS, older versions (<=10.9) lack support for linkat while newer + // versions have it. We want to use linkat if it is available, so we use weak! + // to check. `linkat` is preferable to `link` ecause it gives us a flag to + // specify how symlinks should be handled. We pass 0 as the flags argument, + // meaning it shouldn't follow symlinks. + weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); + + if let Some(f) = linkat.get() { + cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; + } else { + cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + }; } else { - // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives - // us a flag to specify how symlinks should be handled. Pass 0 as - // the flags argument, meaning don't follow symlinks. + // Where we can, use `linkat` instead of `link`; see the comment above + // this one for details on why. cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; } } @@ -1278,7 +1306,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { fn fclonefileat( srcfd: libc::c_int, dst_dirfd: libc::c_int, - dst: *const libc::c_char, + dst: *const c_char, flags: libc::c_int ) -> libc::c_int } diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 9687576bb6a..a6b43229ba6 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; +use crate::sys::weak::syscall; use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; #[cfg(test)] diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 71fec79347a..d3c874edf2d 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -23,6 +23,9 @@ use crate::sys::memchr; use crate::sys_common::rwlock::{StaticRWLock, StaticRWLockReadGuard}; use crate::vec; +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] +use crate::sys::weak::weak; + use libc::{c_char, c_int, c_void}; const TMPBUF_SZ: usize = 128; diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index ed55e1aa715..c888dd0d87d 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -9,6 +9,14 @@ use crate::sys; use crate::sys::cvt; use crate::sys::process::process_common::*; +#[cfg(any( + target_os = "macos", + target_os = "freebsd", + all(target_os = "linux", target_env = "gnu"), + all(target_os = "linux", target_env = "musl"), +))] +use crate::sys::weak::weak; + #[cfg(target_os = "vxworks")] use libc::RTP_ID as pid_t; diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 44f9eabc319..32895001a65 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -26,6 +26,9 @@ mod imp { use crate::io::Read; #[cfg(any(target_os = "linux", target_os = "android"))] + use crate::sys::weak::syscall; + + #[cfg(any(target_os = "linux", target_os = "android"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { // A weak symbol allows interposition, e.g. for perf measurements that want to // disable randomness for consistency. Otherwise, we'll try a raw syscall. @@ -108,6 +111,7 @@ mod imp { use crate::fs::File; use crate::io::Read; use crate::sys::os::errno; + use crate::sys::weak::weak; use libc::{c_int, c_void, size_t}; fn getentropy_fill_bytes(v: &mut [u8]) -> bool { diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index df2ba0a8bc8..879d7160524 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -7,6 +7,8 @@ use crate::ptr; use crate::sys::{os, stack_overflow}; use crate::time::Duration; +#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))] +use crate::sys::weak::weak; #[cfg(not(any(target_os = "l4re", target_os = "vxworks")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs index 432fe4c33bc..cad8be6d289 100644 --- a/library/std/src/sys/unix/weak.rs +++ b/library/std/src/sys/unix/weak.rs @@ -26,8 +26,11 @@ use crate::marker; use crate::mem; use crate::sync::atomic::{self, AtomicUsize, Ordering}; -macro_rules! weak { +// Temporary null documentation to work around #57569 until the fix is beta +#[cfg_attr(bootstrap, doc = "")] +pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( + #[allow(non_upper_case_globals)] static $name: crate::sys::weak::Weak<unsafe extern "C" fn($($t),*) -> $ret> = crate::sys::weak::Weak::new(concat!(stringify!($name), '\0')); ) @@ -100,8 +103,10 @@ unsafe fn fetch(name: &str) -> usize { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize } +// Temporary null documentation to work around #57569 until the fix is beta +#[cfg_attr(bootstrap, doc = "")] #[cfg(not(any(target_os = "linux", target_os = "android")))] -macro_rules! syscall { +pub(crate) macro syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name: $t),*) -> $ret { use super::os; @@ -118,10 +123,12 @@ macro_rules! syscall { ) } +#[cfg_attr(bootstrap, doc = "")] #[cfg(any(target_os = "linux", target_os = "android"))] -macro_rules! syscall { +pub(crate) macro syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name:$t),*) -> $ret { + use weak; // This looks like a hack, but concat_idents only accepts idents // (not paths). use libc::*; diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index b082e21ab3b..0fdf72c8067 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -137,7 +137,7 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> { pub struct Command { program: OsString, - args: Vec<OsString>, + args: Vec<Arg>, env: CommandEnv, cwd: Option<OsString>, flags: u32, @@ -161,6 +161,14 @@ pub struct StdioPipes { pub stderr: Option<AnonPipe>, } +#[derive(Debug)] +enum Arg { + /// Add quotes (if needed) + Regular(OsString), + /// Append raw string without quoting + Raw(OsString), +} + impl Command { pub fn new(program: &OsStr) -> Command { Command { @@ -178,7 +186,7 @@ impl Command { } pub fn arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_os_string()) + self.args.push(Arg::Regular(arg.to_os_string())) } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env @@ -203,6 +211,10 @@ impl Command { self.force_quotes_enabled = enabled; } + pub fn raw_arg(&mut self, command_str_to_append: &OsStr) { + self.args.push(Arg::Raw(command_str_to_append.to_os_string())) + } + pub fn get_program(&self) -> &OsStr { &self.program } @@ -315,9 +327,13 @@ impl Command { impl fmt::Debug for Command { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.program)?; + self.program.fmt(f)?; for arg in &self.args { - write!(f, " {:?}", arg)?; + f.write_str(" ")?; + match arg { + Arg::Regular(s) => s.fmt(f), + Arg::Raw(s) => f.write_str(&s.to_string_lossy()), + }?; } Ok(()) } @@ -536,44 +552,63 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION { } } +enum Quote { + // Every arg is quoted + Always, + // Whitespace and empty args are quoted + Auto, + // Arg appended without any changes (#29494) + Never, +} + // Produces a wide string *without terminating null*; returns an error if // `prog` or any of the `args` contain a nul. -fn make_command_line(prog: &OsStr, args: &[OsString], force_quotes: bool) -> io::Result<Vec<u16>> { +fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> { // Encode the command and arguments in a command line string such // that the spawned process may recover them using CommandLineToArgvW. let mut cmd: Vec<u16> = Vec::new(); // Always quote the program name so CreateProcess doesn't interpret args as // part of the name if the binary wasn't found first time. - append_arg(&mut cmd, prog, true)?; + append_arg(&mut cmd, prog, Quote::Always)?; for arg in args { cmd.push(' ' as u16); - append_arg(&mut cmd, arg, force_quotes)?; + let (arg, quote) = match arg { + Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }), + Arg::Raw(arg) => (arg, Quote::Never), + }; + append_arg(&mut cmd, arg, quote)?; } return Ok(cmd); - fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> io::Result<()> { + fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, quote: Quote) -> io::Result<()> { // If an argument has 0 characters then we need to quote it to ensure // that it actually gets passed through on the command line or otherwise // it will be dropped entirely when parsed on the other end. ensure_no_nuls(arg)?; let arg_bytes = &arg.as_inner().inner.as_inner(); - let quote = force_quotes - || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') - || arg_bytes.is_empty(); + let (quote, escape) = match quote { + Quote::Always => (true, true), + Quote::Auto => { + (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true) + } + Quote::Never => (false, false), + }; if quote { cmd.push('"' as u16); } let mut backslashes: usize = 0; for x in arg.encode_wide() { - if x == '\\' as u16 { - backslashes += 1; - } else { - if x == '"' as u16 { - // Add n+1 backslashes to total 2n+1 before internal '"'. - cmd.extend((0..=backslashes).map(|_| '\\' as u16)); + if escape { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n+1 backslashes to total 2n+1 before internal '"'. + cmd.extend((0..=backslashes).map(|_| '\\' as u16)); + } + backslashes = 0; } - backslashes = 0; } cmd.push(x); } @@ -626,13 +661,15 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> { } pub struct CommandArgs<'a> { - iter: crate::slice::Iter<'a, OsString>, + iter: crate::slice::Iter<'a, Arg>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; fn next(&mut self) -> Option<&'a OsStr> { - self.iter.next().map(|s| s.as_ref()) + self.iter.next().map(|arg| match arg { + Arg::Regular(s) | Arg::Raw(s) => s.as_ref(), + }) } fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs index ff3f9131cc8..3b65856dcac 100644 --- a/library/std/src/sys/windows/process/tests.rs +++ b/library/std/src/sys/windows/process/tests.rs @@ -1,14 +1,35 @@ use super::make_command_line; +use super::Arg; use crate::env; use crate::ffi::{OsStr, OsString}; use crate::process::Command; #[test] +fn test_raw_args() { + let command_line = &make_command_line( + OsStr::new("quoted exe"), + &[ + Arg::Regular(OsString::from("quote me")), + Arg::Raw(OsString::from("quote me *not*")), + Arg::Raw(OsString::from("\t\\")), + Arg::Raw(OsString::from("internal \\\"backslash-\"quote")), + Arg::Regular(OsString::from("optional-quotes")), + ], + false, + ) + .unwrap(); + assert_eq!( + String::from_utf16(command_line).unwrap(), + "\"quoted exe\" \"quote me\" quote me *not* \t\\ internal \\\"backslash-\"quote optional-quotes" + ); +} + +#[test] fn test_make_command_line() { fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String { let command_line = &make_command_line( OsStr::new(prog), - &args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(), + &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(), force_quotes, ) .unwrap(); @@ -17,6 +38,11 @@ fn test_make_command_line() { assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc"); + assert_eq!(test_wrapper("prog", &[r"C:\"], false), r#""prog" C:\"#); + assert_eq!(test_wrapper("prog", &[r"2slashes\\"], false), r#""prog" 2slashes\\"#); + assert_eq!(test_wrapper("prog", &[r" C:\"], false), r#""prog" " C:\\""#); + assert_eq!(test_wrapper("prog", &[r" 2slashes\\"], false), r#""prog" " 2slashes\\\\""#); + assert_eq!( test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false), "\"C:\\Program Files\\blah\\blah.exe\" aaa" diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f7e79141903..9f7e6b95dfb 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -651,22 +651,23 @@ pub fn current() -> Thread { /// Cooperatively gives up a timeslice to the OS scheduler. /// -/// This is used when the programmer knows that the thread will have nothing -/// to do for some time, and thus avoid wasting computing time. +/// This calls the underlying OS scheduler's yield primitive, signaling +/// that the calling thread is willing to give up its remaining timeslice +/// so that the OS may schedule other threads on the CPU. /// -/// For example when polling on a resource, it is common to check that it is -/// available, and if not to yield in order to avoid busy waiting. +/// A drawback of yielding in a loop is that if the OS does not have any +/// other ready threads to run on the current CPU, the thread will effectively +/// busy-wait, which wastes CPU time and energy. /// -/// Thus the pattern of `yield`ing after a failed poll is rather common when -/// implementing low-level shared resources or synchronization primitives. +/// Therefore, when waiting for events of interest, a programmer's first +/// choice should be to use synchronization devices such as [`channel`]s, +/// [`Condvar`]s, [`Mutex`]es or [`join`] since these primitives are +/// implemented in a blocking manner, giving up the CPU until the event +/// of interest has occurred which avoids repeated yielding. /// -/// However programmers will usually prefer to use [`channel`]s, [`Condvar`]s, -/// [`Mutex`]es or [`join`] for their synchronization routines, as they avoid -/// thinking about thread scheduling. -/// -/// Note that [`channel`]s for example are implemented using this primitive. -/// Indeed when you call `send` or `recv`, which are blocking, they will yield -/// if the channel is not available. +/// `yield_now` should thus be used only rarely, mostly in situations where +/// repeated polling is required because there is no other suitable way to +/// learn when an event of interest has occurred. /// /// # Examples /// @@ -998,11 +999,12 @@ impl ThreadId { static mut COUNTER: u64 = 1; unsafe { - let _guard = GUARD.lock(); + let guard = GUARD.lock(); // If we somehow use up all our bits, panic so that we're not // covering up subtle bugs of IDs being reused. if COUNTER == u64::MAX { + drop(guard); // in case the panic handler ends up calling `ThreadId::new()`, avoid reentrant lock acquire. panic!("failed to generate unique thread ID: bitspace exhausted"); } |
