diff options
Diffstat (limited to 'library/std/src')
32 files changed, 471 insertions, 161 deletions
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ec774e62deb..1eae7fa6a95 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -336,7 +336,6 @@ fn default_alloc_error_hook(layout: Layout) { static __rust_alloc_error_handler_should_panic: u8; } - #[allow(unused_unsafe)] if unsafe { __rust_alloc_error_handler_should_panic != 0 } { panic!("memory allocation of {} bytes failed", layout.size()); } else { diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 7543ffadd41..f0e199fac73 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -89,12 +89,11 @@ mod tests; // a backtrace or actually symbolizing it. use crate::backtrace_rs::{self, BytesOrWideString}; -use crate::cell::UnsafeCell; use crate::env; use crate::ffi::c_void; use crate::fmt; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use crate::sync::Once; +use crate::sync::LazyLock; use crate::sys_common::backtrace::{lock, output_filename}; use crate::vec::Vec; @@ -133,12 +132,11 @@ pub enum BacktraceStatus { enum Inner { Unsupported, Disabled, - Captured(LazilyResolvedCapture), + Captured(LazyLock<Capture, LazyResolve>), } struct Capture { actual_start: usize, - resolved: bool, frames: Vec<BacktraceFrame>, } @@ -179,7 +177,7 @@ impl fmt::Debug for Backtrace { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("<unsupported>"), Inner::Disabled => return fmt.write_str("<disabled>"), - Inner::Captured(c) => c.force(), + Inner::Captured(c) => &**c, }; let frames = &capture.frames[capture.actual_start..]; @@ -347,11 +345,10 @@ impl Backtrace { let inner = if frames.is_empty() { Inner::Unsupported } else { - Inner::Captured(LazilyResolvedCapture::new(Capture { + Inner::Captured(LazyLock::new(lazy_resolve(Capture { actual_start: actual_start.unwrap_or(0), frames, - resolved: false, - })) + }))) }; Backtrace { inner } @@ -376,7 +373,7 @@ impl<'a> Backtrace { #[must_use] #[unstable(feature = "backtrace_frames", issue = "79676")] pub fn frames(&'a self) -> &'a [BacktraceFrame] { - if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] } + if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] } } } @@ -386,7 +383,7 @@ impl fmt::Display for Backtrace { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("unsupported backtrace"), Inner::Disabled => return fmt.write_str("disabled backtrace"), - Inner::Captured(c) => c.force(), + Inner::Captured(c) => &**c, }; let full = fmt.alternate(); @@ -430,46 +427,15 @@ impl fmt::Display for Backtrace { } } -struct LazilyResolvedCapture { - sync: Once, - capture: UnsafeCell<Capture>, -} - -impl LazilyResolvedCapture { - fn new(capture: Capture) -> Self { - LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) } - } - - fn force(&self) -> &Capture { - self.sync.call_once(|| { - // SAFETY: This exclusive reference can't overlap with any others - // `Once` guarantees callers will block until this closure returns - // `Once` also guarantees only a single caller will enter this closure - unsafe { &mut *self.capture.get() }.resolve(); - }); - - // SAFETY: This shared reference can't overlap with the exclusive reference above - unsafe { &*self.capture.get() } - } -} - -// SAFETY: Access to the inner value is synchronized using a thread-safe `Once` -// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too -unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {} - -impl Capture { - fn resolve(&mut self) { - // If we're already resolved, nothing to do! - if self.resolved { - return; - } - self.resolved = true; +type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync; +fn lazy_resolve(mut capture: Capture) -> LazyResolve { + move || { // Use the global backtrace lock to synchronize this as it's a // requirement of the `backtrace` crate, and then actually resolve // everything. let _lock = lock(); - for frame in self.frames.iter_mut() { + for frame in capture.frames.iter_mut() { let symbols = &mut frame.symbols; let frame = match &frame.frame { RawFrame::Actual(frame) => frame, @@ -490,6 +456,8 @@ impl Capture { }); } } + + capture } } diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs index 4dfbf88e83e..ef419806dcc 100644 --- a/library/std/src/backtrace/tests.rs +++ b/library/std/src/backtrace/tests.rs @@ -43,9 +43,8 @@ fn generate_fake_frames() -> Vec<BacktraceFrame> { #[test] fn test_debug() { let backtrace = Backtrace { - inner: Inner::Captured(LazilyResolvedCapture::new(Capture { + inner: Inner::Captured(LazyLock::preinit(Capture { actual_start: 1, - resolved: true, frames: generate_fake_frames(), })), }; @@ -66,9 +65,8 @@ fn test_debug() { #[test] fn test_frames() { let backtrace = Backtrace { - inner: Inner::Captured(LazilyResolvedCapture::new(Capture { + inner: Inner::Captured(LazyLock::preinit(Capture { actual_start: 1, - resolved: true, frames: generate_fake_frames(), })), }; diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index e7bad9d542c..67e58fd1b86 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -141,6 +141,51 @@ impl OsString { OsString { inner: Buf::from_string(String::new()) } } + /// Converts bytes to an `OsString` without checking that the bytes contains + /// valid [`OsStr`]-encoded data. + /// + /// 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 + /// ASCII. + /// + /// See the [module's toplevel documentation about conversions][conversions] for safe, + /// cross-platform [conversions] from/to native representations. + /// + /// # Safety + /// + /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of + /// validated UTF-8 and bytes from [`OsStr::as_os_str_bytes`] from within the same rust version + /// built for the same target platform. For example, reconstructing an `OsString` from bytes sent + /// over the network or stored in a file will likely violate these safety rules. + /// + /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_os_str_bytes`] can be + /// split either immediately before or immediately after any valid non-empty UTF-8 substring. + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_bytes)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("Mary had a little lamb"); + /// let bytes = os_str.as_os_str_bytes(); + /// let words = bytes.split(|b| *b == b' '); + /// let words: Vec<&OsStr> = words.map(|word| { + /// // SAFETY: + /// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes` + /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring + /// unsafe { OsStr::from_os_str_bytes_unchecked(word) } + /// }).collect(); + /// ``` + /// + /// [conversions]: super#conversions + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub unsafe fn from_os_str_bytes_unchecked(bytes: Vec<u8>) -> Self { + OsString { inner: Buf::from_os_str_bytes_unchecked(bytes) } + } + /// Converts to an [`OsStr`] slice. /// /// # Examples @@ -159,6 +204,26 @@ impl OsString { self } + /// Converts the `OsString` into a byte slice. To convert the byte slice back into an + /// `OsString`, use the [`OsStr::from_os_str_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 + /// ASCII. + /// + /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should + /// be treated as opaque and only comparable within the same rust version built for the same + /// target platform. For example, sending the bytes over the network or storing it in a file + /// will likely result in incompatible data. See [`OsString`] for more encoding details + /// and [`std::ffi`] for platform-specific, specified conversions. + /// + /// [`std::ffi`]: crate::ffi + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner.into_os_str_bytes() + } + /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. /// /// On failure, ownership of the original `OsString` is returned. diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9ff01b9c35d..d74f0f00e46 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -957,6 +957,7 @@ fn readlink_not_symlink() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); @@ -1453,6 +1454,7 @@ fn metadata_access_times() { /// Test creating hard links to symlinks. #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn symlink_hard_link() { let tmpdir = tmpdir(); if !got_symlink_permission(&tmpdir) { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index f076ee0923c..3840ffe7eec 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -8,24 +8,41 @@ use crate::io::{ self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, }; -/// A reader which is always at EOF. +/// `Empty` ignores any data written via [`Write`], and will always be empty +/// (returning zero bytes) when read via [`Read`]. /// -/// This struct is generally created by calling [`empty()`]. Please see -/// the documentation of [`empty()`] for more details. +/// This struct is generally created by calling [`empty()`]. Please +/// see the documentation of [`empty()`] for more details. #[stable(feature = "rust1", since = "1.0.0")] #[non_exhaustive] -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Debug, Default)] pub struct Empty; -/// Constructs a new handle to an empty reader. +/// Creates a value that is always at EOF for reads, and ignores all data written. /// -/// All reads from the returned reader will return <code>[Ok]\(0)</code>. +/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] +/// and the contents of the buffer will not be inspected. +/// +/// All calls to [`read`] from the returned reader will return [`Ok(0)`]. +/// +/// [`Ok(buf.len())`]: Ok +/// [`Ok(0)`]: Ok +/// +/// [`write`]: Write::write +/// [`read`]: Read::read /// /// # Examples /// -/// A slightly sad example of not reading anything into a buffer: +/// ```rust +/// use std::io::{self, Write}; /// +/// let buffer = vec![1, 2, 3, 5, 8]; +/// let num_bytes = io::empty().write(&buffer).unwrap(); +/// assert_eq!(num_bytes, 5); /// ``` +/// +/// +/// ```rust /// use std::io::{self, Read}; /// /// let mut buffer = String::new(); @@ -76,13 +93,6 @@ impl Seek for Empty { } } -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Empty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Empty").finish_non_exhaustive() - } -} - impl SizeHint for Empty { #[inline] fn upper_bound(&self) -> Option<usize> { @@ -90,6 +100,54 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "CURRENT_RUSTC_VERSION")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "CURRENT_RUSTC_VERSION")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// A reader which yields one byte over and over and over and over and over and... /// /// This struct is generally created by calling [`repeat()`]. Please @@ -182,19 +240,20 @@ impl fmt::Debug for Repeat { /// A writer which will move data into the void. /// -/// This struct is generally created by calling [`sink`]. Please +/// This struct is generally created by calling [`sink()`]. Please /// see the documentation of [`sink()`] for more details. #[stable(feature = "rust1", since = "1.0.0")] #[non_exhaustive] -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Debug, Default)] pub struct Sink; /// Creates an instance of a writer which will successfully consume all data. /// -/// All calls to [`write`] on the returned instance will return `Ok(buf.len())` +/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// /// [`write`]: Write::write +/// [`Ok(buf.len())`]: Ok /// /// # Examples /// @@ -259,10 +318,3 @@ impl Write for &Sink { Ok(()) } } - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Sink { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sink").finish_non_exhaustive() - } -} diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 1baa94e64c9..6de91e29c77 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -18,7 +18,7 @@ fn empty_reads() { assert_eq!(e.read(&mut []).unwrap(), 0); assert_eq!(e.read(&mut [0]).unwrap(), 0); assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); - assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); + assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0); let buf: &mut [MaybeUninit<_>] = &mut []; let mut buf: BorrowedBuf<'_> = buf.into(); @@ -40,7 +40,7 @@ fn empty_reads() { let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); - e.by_ref().read_buf(buf.unfilled()).unwrap(); + Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); assert_eq!(buf.init_len(), 0); } @@ -66,6 +66,15 @@ fn empty_seeks() { } #[test] +fn empty_sinks() { + let mut e = empty(); + assert_eq!(e.write(&[]).unwrap(), 0); + assert_eq!(e.write(&[0]).unwrap(), 1); + assert_eq!(e.write(&[0; 1024]).unwrap(), 1024); + assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024); +} + +#[test] fn repeat_repeats() { let mut r = repeat(4); let mut b = [0; 1024]; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5bf66850f03..238c74229cc 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -272,6 +272,7 @@ #![feature(staged_api)] #![feature(thread_local)] #![feature(try_blocks)] +#![feature(type_alias_impl_trait)] #![feature(utf8_chunks)] // tidy-alphabetical-end // @@ -542,7 +543,7 @@ pub mod time; // Pull in `std_float` crate into std. The contents of // `std_float` are in a different repository: rust-lang/portable-simd. #[path = "../../portable-simd/crates/std_float/src/lib.rs"] -#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn)] #[allow(rustdoc::bare_urls)] #[unstable(feature = "portable_simd", issue = "86656")] mod std_float; diff --git a/library/std/src/os/android/raw.rs b/library/std/src/os/android/raw.rs index daaf3d3eac6..175f8eac971 100644 --- a/library/std/src/os/android/raw.rs +++ b/library/std/src/os/android/raw.rs @@ -89,7 +89,7 @@ mod arch { } } -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] mod arch { use crate::os::raw::{c_int, c_long, c_uint, c_ulong}; use crate::os::unix::raw::{gid_t, uid_t}; diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs index 19d0ffb2e39..5b302e3c2c8 100644 --- a/library/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -9,11 +9,6 @@ macro_rules! alias_core_ffi { ($($t:ident)*) => {$( #[stable(feature = "raw_os", since = "1.1.0")] #[doc = include_str!(concat!("../../../../core/src/ffi/", stringify!($t), ".md"))] - // Make this type alias appear cfg-dependent so that Clippy does not suggest - // replacing expressions like `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be - // removed after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093 - // is fixed. - #[cfg(all())] #[doc(cfg(all()))] pub type $t = core::ffi::$t; )*} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 1e1c3693105..88326aaf295 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -149,7 +149,36 @@ pub trait FileExt { /// Note that similar to [`File::write`], it is not an error to return a /// short write. /// + /// # Bug + /// On some systems, `write_at` utilises [`pwrite64`] to write to files. + /// However, this syscall has a [bug] where files opened with the `O_APPEND` + /// flag fail to respect the offset parameter, always appending to the end + /// of the file instead. + /// + /// It is possible to inadvertantly set this flag, like in the example below. + /// Therefore, it is important to be vigilant while changing options to mitigate + /// unexpected behaviour. + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// // Open a file with the append option (sets the `O_APPEND` flag) + /// let file = File::options().append(true).open("foo.txt")?; + /// + /// // We attempt to write at offset 10; instead appended to EOF + /// file.write_at(b"sushi", 10)?; + /// + /// // foo.txt is 5 bytes long instead of 15 + /// Ok(()) + /// } + /// ``` + /// /// [`File::write`]: fs::File::write + /// [`pwrite64`]: https://man7.org/linux/man-pages/man2/pwrite.2.html + /// [bug]: https://man7.org/linux/man-pages/man2/pwrite.2.html#BUGS /// /// # Examples /// @@ -159,7 +188,7 @@ pub trait FileExt { /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; + /// let file = File::create("foo.txt")?; /// /// // We now write at the offset 10. /// file.write_at(b"sushi", 10)?; @@ -971,7 +1000,6 @@ impl DirBuilderExt for fs::DirBuilder { /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -979,7 +1007,7 @@ impl DirBuilderExt for fs::DirBuilder { /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")] pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -991,7 +1019,6 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io:: /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1000,7 +1027,7 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io:: /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")] pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -1013,7 +1040,6 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result< /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1021,7 +1047,7 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result< /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "CURRENT_RUSTC_VERSION")] pub fn lchown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 39f10c50dc4..3d4302e665c 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -23,6 +23,7 @@ macro_rules! or_panic { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn basic() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -93,6 +94,7 @@ fn pair() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn try_clone() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -119,6 +121,7 @@ fn try_clone() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn iter() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -168,6 +171,7 @@ fn long_path() { #[test] #[cfg(not(target_os = "nto"))] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -195,6 +199,7 @@ fn timeouts() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -214,6 +219,7 @@ fn test_read_timeout() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_with_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -241,6 +247,7 @@ fn test_read_with_timeout() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_timeout_zero_duration() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -260,6 +267,7 @@ fn test_unix_stream_timeout_zero_duration() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -276,6 +284,7 @@ fn test_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unnamed_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -293,6 +302,7 @@ fn test_unnamed_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_connect_to_recv_addr() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -317,6 +327,7 @@ fn test_unix_datagram_connect_to_recv_addr() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_connect_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -343,6 +354,7 @@ fn test_connect_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_recv() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -385,6 +397,7 @@ fn datagram_pair() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_timeout_zero_duration() { let dir = tmpdir(); let path = dir.path().join("sock"); @@ -529,6 +542,7 @@ fn test_abstract_no_pathname_and_not_unnamed() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_peek() { let (txdone, rxdone) = crate::sync::mpsc::channel(); @@ -561,6 +575,7 @@ fn test_unix_stream_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -585,6 +600,7 @@ fn test_unix_datagram_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek_from() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -648,6 +664,7 @@ fn test_send_vectored_fds_unix_stream() { #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_to_unix_datagram() { fn getpid() -> libc::pid_t { unsafe { libc::getpid() } @@ -715,6 +732,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 0e90d618ad4..15285465c71 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -300,7 +300,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path }; if let Some(path) = path - && let Ok(mut out) = crate::fs::File::options().create(true).write(true).open(&path) + && let Ok(mut out) = crate::fs::File::options().create(true).append(true).open(&path) { write(&mut out, BacktraceStyle::full()); } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 28cd3c4e4db..99f7a60f8ab 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2608,9 +2608,27 @@ impl Path { } fn _with_extension(&self, extension: &OsStr) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.set_extension(extension); - buf + let self_len = self.as_os_str().len(); + let self_bytes = self.as_os_str().as_os_str_bytes(); + + let (new_capacity, slice_to_copy) = match self.extension() { + None => { + // Enough capacity for the extension and the dot + let capacity = self_len + extension.len() + 1; + let whole_path = self_bytes.iter(); + (capacity, whole_path) + } + Some(previous_extension) => { + let capacity = self_len + extension.len() - previous_extension.len(); + let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter(); + (capacity, path_till_dot) + } + }; + + let mut new_path = PathBuf::with_capacity(new_capacity); + new_path.as_mut_vec().extend(slice_to_copy); + new_path.set_extension(extension); + new_path } /// Produces an iterator over the [`Component`]s of the path. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index dd307022c6d..f12ffbf2e01 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1183,7 +1183,7 @@ pub fn test_prefix_ext() { #[test] pub fn test_push() { macro_rules! tp ( - ($path:expr, $push:expr, $expected:expr) => ( { + ($path:expr, $push:expr, $expected:expr) => ({ let mut actual = PathBuf::from($path); actual.push($push); assert!(actual.to_str() == Some($expected), @@ -1281,7 +1281,7 @@ pub fn test_push() { #[test] pub fn test_pop() { macro_rules! tp ( - ($path:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $expected:expr, $output:expr) => ({ let mut actual = PathBuf::from($path); let output = actual.pop(); assert!(actual.to_str() == Some($expected) && output == $output, @@ -1335,7 +1335,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { macro_rules! tfn ( - ($path:expr, $file:expr, $expected:expr) => ( { + ($path:expr, $file:expr, $expected:expr) => ({ let mut p = PathBuf::from($path); p.set_file_name($file); assert!(p.to_str() == Some($expected), @@ -1369,7 +1369,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { macro_rules! tfe ( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ let mut p = PathBuf::from($path); let output = p.set_extension($ext); assert!(p.to_str() == Some($expected) && output == $output, @@ -1395,6 +1395,46 @@ pub fn test_set_extension() { } #[test] +pub fn test_with_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); +} + +#[test] fn test_eq_receivers() { use crate::borrow::Cow; @@ -1669,7 +1709,7 @@ fn into_rc() { #[test] fn test_ord() { macro_rules! ord( - ($ord:ident, $left:expr, $right:expr) => ( { + ($ord:ident, $left:expr, $right:expr) => ({ use core::cmp::Ordering; let left = Path::new($left); diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 76a1b4a2a86..9c4b926b7ec 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -21,11 +21,11 @@ impl WaitTimeoutResult { /// /// # Examples /// - /// This example spawns a thread which will update the boolean value and - /// then wait 100 milliseconds before notifying the condvar. + /// This example spawns a thread which will sleep 20 milliseconds before + /// updating a boolean value and then notifying the condvar. /// - /// The main thread will wait with a timeout on the condvar and then leave - /// once the boolean has been updated and notified. + /// The main thread will wait with a 10 millisecond timeout on the condvar + /// and will leave the loop upon timeout. /// /// ``` /// use std::sync::{Arc, Condvar, Mutex}; @@ -49,14 +49,12 @@ impl WaitTimeoutResult { /// /// // Wait for the thread to start up. /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); /// loop { /// // Let's put a timeout on the condvar's wait. - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. + /// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed. + /// if result.1.timed_out() { + /// // timed out now and we can leave. /// break /// } /// } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index a6bc468b092..1aeed406562 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -25,6 +25,8 @@ union Data<T, F> { /// /// # Examples /// +/// Initialize static variables with `LazyLock`. +/// /// ``` /// #![feature(lazy_cell)] /// @@ -54,6 +56,24 @@ union Data<T, F> { /// // Some("Hoyten") /// } /// ``` +/// Initialize fields with `LazyLock`. +/// ``` +/// #![feature(lazy_cell)] +/// +/// use std::sync::LazyLock; +/// +/// #[derive(Debug)] +/// struct UseCellLock { +/// number: LazyLock<u32>, +/// } +/// fn main() { +/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32); +/// +/// let data = UseCellLock { number: lock }; +/// println!("{}", *data.number); +/// } +/// ``` + #[unstable(feature = "lazy_cell", issue = "109736")] pub struct LazyLock<T, F = fn() -> T> { once: Once, @@ -69,6 +89,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } } + /// Creates a new lazy value that is already initialized. + #[inline] + #[cfg(test)] + pub(crate) fn preinit(value: T) -> LazyLock<T, F> { + let once = Once::new(); + once.call_once(|| {}); + LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) } + } + /// Consumes this `LazyLock` returning the stored value. /// /// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise. diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index bc5da1a1896..c0a9619bf7b 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -87,10 +87,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::<$t>::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs index 5d48ce1e03b..7cf29192122 100644 --- a/library/std/src/sys/common/thread_local/os_local.rs +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -24,7 +24,6 @@ pub macro thread_local_inner { const fn __init() -> $t { INIT_EXPR } static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = _init { @@ -59,10 +58,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs index 80322a97864..5cb6c541a0e 100644 --- a/library/std/src/sys/common/thread_local/static_local.rs +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -43,10 +43,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs index 1608b8cb642..7ac9d1d64b4 100644 --- a/library/std/src/sys/sgx/thread.rs +++ b/library/std/src/sys/sgx/thread.rs @@ -121,8 +121,16 @@ impl Thread { rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } + /// SGX should protect in-enclave data from the outside (attacker), + /// so there should be no data leakage to the OS, + /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. + /// + /// This is why the method is intentionally No-Op. pub fn set_name(_name: &CStr) { - // FIXME: could store this pointer in TLS somewhere + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic (target-agnostic) Rust thread code. + // This can be observed in the [`std::thread::tests::test_named_thread`] test, + // which succeeds as-is with the SGX target. } pub fn sleep(dur: Duration) { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 326f1481e19..77ef086f29b 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -110,6 +110,11 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 { match errno() { libc::EINTR => continue, + #[cfg(target_vendor = "unikraft")] + libc::ENOSYS => { + // Not all configurations of Unikraft enable `LIBPOSIX_EVENT`. + break 'poll; + } libc::EINVAL | libc::EAGAIN | libc::ENOMEM => { // RLIMIT_NOFILE or temporary allocation failures // may be preventing use of poll(), fall back to fcntl @@ -165,7 +170,14 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] + #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", + // Unikraft's `signal` implementation is currently broken: + // https://github.com/unikraft/lib-musl/issues/57 + target_vendor = "unikraft", + )))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs index f7333fd5a1f..463b0a27515 100644 --- a/library/std/src/sys/unix/os_str.rs +++ b/library/std/src/sys/unix/os_str.rs @@ -96,6 +96,16 @@ impl AsInner<[u8]> for Buf { } impl Buf { + #[inline] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self { + Self { inner: s } + } + pub fn from_string(s: String) -> Buf { Buf { inner: s.into_bytes() } } diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index d471be33ed5..fbf158f56fc 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -17,7 +17,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "openbsd"), - not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), not(target_os = "redox"), @@ -68,11 +67,25 @@ mod imp { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "freebsd")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + // FIXME: using the above when libary std's libc is updated + extern "C" { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" )))] fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false @@ -82,7 +95,8 @@ mod imp { target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" ))] fn getrandom_fill_bytes(v: &mut [u8]) -> bool { use crate::sync::atomic::{AtomicBool, Ordering}; @@ -222,7 +236,7 @@ mod imp { } } -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] +#[cfg(target_os = "netbsd")] mod imp { use crate::ptr; diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 4fe95d41116..e28dd493536 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -69,10 +69,7 @@ unsafe extern "C" fn init() { /// Helper macro for creating CStrs from literals and symbol names. macro_rules! ansi_str { - (sym $ident:ident) => {{ - #[allow(unused_unsafe)] - crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) - }}; + (sym $ident:ident) => {{ crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) }}; ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }}; } diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 16c4f55c687..4708657a907 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -63,6 +63,16 @@ impl fmt::Display for Slice { } impl Buf { + #[inline] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner.into_bytes() + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self { + Self { inner: Wtf8Buf::from_bytes_unchecked(s) } + } + pub fn with_capacity(capacity: usize) -> Buf { Buf { inner: Wtf8Buf::with_capacity(capacity) } } diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs index 9707a95dff2..cf542d2bfb8 100644 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ b/library/std/src/sys/windows/thread_local_dtor.rs @@ -4,29 +4,4 @@ #![unstable(feature = "thread_local_internals", issue = "none")] #![cfg(target_thread_local)] -// Using a per-thread list avoids the problems in synchronizing global state. -#[thread_local] -static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); - -// Ensure this can never be inlined because otherwise this may break in dylibs. -// See #44391. -#[inline(never)] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - DESTRUCTORS.push((t, dtor)); -} - -#[inline(never)] // See comment above -/// Runs destructors. This should not be called until thread exit. -pub unsafe fn run_keyless_dtors() { - // Drop all the destructors. - // - // Note: While this is potentially an infinite loop, it *should* be - // the case that this loop always terminates because we provide the - // guarantee that a TLS key cannot be set after it is flagged for - // destruction. - while let Some((ptr, dtor)) = DESTRUCTORS.pop() { - (dtor)(ptr); - } - // We're done so free the memory. - DESTRUCTORS = Vec::new(); -} +pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index 17628b7579b..036d96596e9 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,7 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{ - AtomicPtr, AtomicU32, + AtomicBool, AtomicPtr, AtomicU32, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; use crate::sys::c; @@ -9,6 +9,41 @@ use crate::sys::c; #[cfg(test)] mod tests; +/// An optimization hint. The compiler is often smart enough to know if an atomic +/// is never set and can remove dead code based on that fact. +static HAS_DTORS: AtomicBool = AtomicBool::new(false); + +// Using a per-thread list avoids the problems in synchronizing global state. +#[thread_local] +#[cfg(target_thread_local)] +static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); + +// Ensure this can never be inlined because otherwise this may break in dylibs. +// See #44391. +#[inline(never)] +#[cfg(target_thread_local)] +pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + DESTRUCTORS.push((t, dtor)); + HAS_DTORS.store(true, Relaxed); +} + +#[inline(never)] // See comment above +#[cfg(target_thread_local)] +/// Runs destructors. This should not be called until thread exit. +unsafe fn run_keyless_dtors() { + // Drop all the destructors. + // + // Note: While this is potentially an infinite loop, it *should* be + // the case that this loop always terminates because we provide the + // guarantee that a TLS key cannot be set after it is flagged for + // destruction. + while let Some((ptr, dtor)) = DESTRUCTORS.pop() { + (dtor)(ptr); + } + // We're done so free the memory. + DESTRUCTORS = Vec::new(); +} + type Key = c::DWORD; type Dtor = unsafe extern "C" fn(*mut u8); @@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static StaticKey) { + // Ensure this is never run when native thread locals are available. + assert_eq!(false, cfg!(target_thread_local)); let this = <*const StaticKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. @@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) { Err(new) => head = new, } } + HAS_DTORS.store(true, Release); } // ------------------------------------------------------------------------- @@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c:: #[allow(dead_code, unused_variables)] unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { + if !HAS_DTORS.load(Acquire) { + return; + } if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + #[cfg(not(target_thread_local))] run_dtors(); #[cfg(target_thread_local)] - super::thread_local_dtor::run_keyless_dtors(); + run_keyless_dtors(); } // See comments above for what this is doing. Note that we don't need this diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs index c95f383fb90..c739f0caf3e 100644 --- a/library/std/src/sys/windows/thread_local_key/tests.rs +++ b/library/std/src/sys/windows/thread_local_key/tests.rs @@ -1,3 +1,7 @@ +// This file only tests the thread local key fallback. +// Windows targets with native thread local support do not use this. +#![cfg(not(target_thread_local))] + use super::StaticKey; use crate::ptr; diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index 6f020940df1..84e2c5d8d7f 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -60,6 +60,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: bt_fmt.add_context()?; let mut idx = 0; let mut res = Ok(()); + let mut omitted_count: usize = 0; + let mut first_omit = true; // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; backtrace_rs::trace_unsynchronized(|frame| { @@ -85,10 +87,27 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: start = true; return; } + if !start { + omitted_count += 1; + } } } if start { + if omitted_count > 0 { + debug_assert!(print_fmt == PrintFmt::Short); + // only print the message between the middle of frames + if !first_omit { + let _ = writeln!( + bt_fmt.formatter(), + " [... omitted {} frame{} ...]", + omitted_count, + if omitted_count > 1 { "s" } else { "" } + ); + } + first_omit = false; + omitted_count = 0; + } res = bt_fmt.frame().symbol(frame, symbol); } }); diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs index 38c9e50009a..88d937a7db1 100644 --- a/library/std/src/sys_common/thread_info.rs +++ b/library/std/src/sys_common/thread_info.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] // stack_guard isn't used right now on all platforms -#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny use crate::cell::RefCell; use crate::sys::thread::guard::Guard; diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index c9d3e13cf0c..195d175cc9b 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -182,6 +182,15 @@ impl Wtf8Buf { Wtf8Buf { bytes: Vec::with_capacity(capacity), is_known_utf8: true } } + /// Creates a WTF-8 string from a WTF-8 byte vec. + /// + /// Since the byte vec is not checked for valid WTF-8, this functions is + /// marked unsafe. + #[inline] + pub unsafe fn from_bytes_unchecked(value: Vec<u8>) -> Wtf8Buf { + Wtf8Buf { bytes: value, is_known_utf8: false } + } + /// Creates a WTF-8 string from a UTF-8 `String`. /// /// This takes ownership of the `String` and does not copy. @@ -402,6 +411,12 @@ impl Wtf8Buf { self.bytes.truncate(new_len) } + /// Consumes the WTF-8 string and tries to convert it to a vec of bytes. + #[inline] + pub fn into_bytes(self) -> Vec<u8> { + self.bytes + } + /// Consumes the WTF-8 string and tries to convert it to UTF-8. /// /// This does not copy the data. |
