diff options
Diffstat (limited to 'library/std/src')
32 files changed, 943 insertions, 269 deletions
diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 043747d0bc5..535236ff89a 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -597,6 +597,13 @@ impl Error for JoinPathsError { /// Returns the path of the current user's home directory if known. /// +/// This may return `None` if getting the directory fails or if the platform does not have user home directories. +/// +/// For storing user data and configuration it is often preferable to use more specific directories. +/// For example, [XDG Base Directories] on Unix or the `LOCALAPPDATA` and `APPDATA` environment variables on Windows. +/// +/// [XDG Base Directories]: https://specifications.freedesktop.org/basedir-spec/latest/ +/// /// # Unix /// /// - Returns the value of the 'HOME' environment variable if it is set diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 03f38e220a5..476c403c21f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -338,9 +338,9 @@ pub enum ErrorKind { /// example, on Unix, a named pipe opened with `File::open`. #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, - /// Filesystem quota was exceeded. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemQuotaExceeded, + /// Filesystem quota or some other kind of quota was exceeded. + #[stable(feature = "io_error_quota_exceeded", since = "CURRENT_RUSTC_VERSION")] + QuotaExceeded, /// File larger than allowed or supported. /// /// This might arise from a hard limit of the underlying filesystem or file access API, or from @@ -364,7 +364,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[unstable(feature = "io_error_more", issue = "86442")] + #[stable(feature = "io_error_crosses_devices", since = "CURRENT_RUSTC_VERSION")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// @@ -446,8 +446,8 @@ pub enum ErrorKind { impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { use ErrorKind::*; - // tidy-alphabetical-start match *self { + // tidy-alphabetical-start AddrInUse => "address in use", AddrNotAvailable => "address not available", AlreadyExists => "entity already exists", @@ -460,12 +460,11 @@ impl ErrorKind { Deadlock => "deadlock", DirectoryNotEmpty => "directory not empty", ExecutableFileBusy => "executable file busy", - FileTooLarge => "file too large", FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", - FilesystemQuotaExceeded => "filesystem quota exceeded", + FileTooLarge => "file too large", HostUnreachable => "host unreachable", - Interrupted => "operation interrupted", InProgress => "in progress", + Interrupted => "operation interrupted", InvalidData => "invalid data", InvalidFilename => "invalid filename", InvalidInput => "invalid input parameter", @@ -479,6 +478,7 @@ impl ErrorKind { Other => "other error", OutOfMemory => "out of memory", PermissionDenied => "permission denied", + QuotaExceeded => "quota exceeded", ReadOnlyFilesystem => "read-only filesystem or storage medium", ResourceBusy => "resource busy", StaleNetworkFileHandle => "stale network file handle", @@ -490,8 +490,8 @@ impl ErrorKind { Unsupported => "unsupported", WouldBlock => "operation would block", WriteZero => "write zero", + // tidy-alphabetical-end } - // tidy-alphabetical-end } } diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index a839a2fbac1..f958a938646 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -335,7 +335,7 @@ fn kind_from_prim(ek: u32) -> Option<ErrorKind> { WriteZero, StorageFull, NotSeekable, - FilesystemQuotaExceeded, + QuotaExceeded, FileTooLarge, ResourceBusy, ExecutableFileBusy, diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 4302e24781e..0c526eafdf3 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -807,64 +807,6 @@ mod in_keyword {} /// [Reference]: ../reference/statements.html#let-statements mod let_keyword {} -#[doc(keyword = "while")] -// -/// Loop while a condition is upheld. -/// -/// A `while` expression is used for predicate loops. The `while` expression runs the conditional -/// expression before running the loop body, then runs the loop body if the conditional -/// expression evaluates to `true`, or exits the loop otherwise. -/// -/// ```rust -/// let mut counter = 0; -/// -/// while counter < 10 { -/// println!("{counter}"); -/// counter += 1; -/// } -/// ``` -/// -/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression -/// cannot break with a value and always evaluates to `()` unlike [`loop`]. -/// -/// ```rust -/// let mut i = 1; -/// -/// while i < 100 { -/// i *= 2; -/// if i == 64 { -/// break; // Exit when `i` is 64. -/// } -/// } -/// ``` -/// -/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` -/// expressions with `while let`. The `while let` expression matches the pattern against the -/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. -/// We can use `break` and `continue` in `while let` expressions just like in `while`. -/// -/// ```rust -/// let mut counter = Some(0); -/// -/// while let Some(i) = counter { -/// if i == 10 { -/// counter = None; -/// } else { -/// println!("{i}"); -/// counter = Some (i + 1); -/// } -/// } -/// ``` -/// -/// For more information on `while` and loops in general, see the [reference]. -/// -/// See also, [`for`], [`loop`]. -/// -/// [`for`]: keyword.for.html -/// [`loop`]: keyword.loop.html -/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops -mod while_keyword {} - #[doc(keyword = "loop")] // /// Loop indefinitely. @@ -1321,10 +1263,10 @@ mod return_keyword {} /// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} -// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can remove the -// three next lines and put back: `#[doc(keyword = "Self")]`. +// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can replace +// these two lines with `#[doc(keyword = "Self")]` and update `is_doc_keyword` in +// `CheckAttrVisitor`. #[doc(alias = "Self")] -#[allow(rustc::existing_doc_keyword)] #[doc(keyword = "SelfTy")] // /// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type @@ -2343,6 +2285,64 @@ mod use_keyword {} /// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/0135-where.md mod where_keyword {} +#[doc(keyword = "while")] +// +/// Loop while a condition is upheld. +/// +/// A `while` expression is used for predicate loops. The `while` expression runs the conditional +/// expression before running the loop body, then runs the loop body if the conditional +/// expression evaluates to `true`, or exits the loop otherwise. +/// +/// ```rust +/// let mut counter = 0; +/// +/// while counter < 10 { +/// println!("{counter}"); +/// counter += 1; +/// } +/// ``` +/// +/// Like the [`for`] expression, we can use `break` and `continue`. A `while` expression +/// cannot break with a value and always evaluates to `()` unlike [`loop`]. +/// +/// ```rust +/// let mut i = 1; +/// +/// while i < 100 { +/// i *= 2; +/// if i == 64 { +/// break; // Exit when `i` is 64. +/// } +/// } +/// ``` +/// +/// As `if` expressions have their pattern matching variant in `if let`, so too do `while` +/// expressions with `while let`. The `while let` expression matches the pattern against the +/// expression, then runs the loop body if pattern matching succeeds, or exits the loop otherwise. +/// We can use `break` and `continue` in `while let` expressions just like in `while`. +/// +/// ```rust +/// let mut counter = Some(0); +/// +/// while let Some(i) = counter { +/// if i == 10 { +/// counter = None; +/// } else { +/// println!("{i}"); +/// counter = Some (i + 1); +/// } +/// } +/// ``` +/// +/// For more information on `while` and loops in general, see the [reference]. +/// +/// See also, [`for`], [`loop`]. +/// +/// [`for`]: keyword.for.html +/// [`loop`]: keyword.loop.html +/// [reference]: ../reference/expressions/loop-expr.html#predicate-loops +mod while_keyword {} + // 2018 Edition keywords #[doc(alias = "promise")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 585946c1d50..1c80694ca8f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -251,7 +251,6 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] -#![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] @@ -292,6 +291,7 @@ #![feature(dropck_eyepatch)] #![feature(f128)] #![feature(f16)] +#![feature(formatting_options)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(lang_items)] @@ -347,7 +347,6 @@ #![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] -#![feature(prelude_2024)] #![feature(ptr_as_uninit)] #![feature(ptr_mask)] #![feature(random)] @@ -373,6 +372,7 @@ #![feature(thin_box)] #![feature(try_reserve_kind)] #![feature(try_with_capacity)] +#![feature(unique_rc_arc)] #![feature(vec_into_raw_parts)] // tidy-alphabetical-end // @@ -544,6 +544,8 @@ pub use core::u64; #[stable(feature = "i128", since = "1.26.0")] #[allow(deprecated, deprecated_in_future)] pub use core::u128; +#[unstable(feature = "unsafe_binders", issue = "130516")] +pub use core::unsafe_binder; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::usize; @@ -589,7 +591,7 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "core_pattern_types", issue = "123646")] +#[unstable(feature = "pattern_type_macro", issue = "123646")] pub mod pat; pub mod path; #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 388b8a88a1a..abb13b75f50 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -428,6 +428,14 @@ impl<T: AsFd + ?Sized> AsFd for crate::rc::Rc<T> { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl<T: AsFd + ?Sized> AsFd for crate::rc::UniqueRc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + #[stable(feature = "asfd_ptrs", since = "1.64.0")] impl<T: AsFd + ?Sized> AsFd for Box<T> { #[inline] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 0d99d5492a2..22f5528248a 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -266,6 +266,14 @@ impl<T: AsRawFd> AsRawFd for crate::rc::Rc<T> { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl<T: AsRawFd + ?Sized> AsRawFd for crate::rc::UniqueRc<T> { + #[inline] + fn as_raw_fd(&self) -> RawFd { + (**self).as_raw_fd() + } +} + #[stable(feature = "asrawfd_ptrs", since = "1.63.0")] impl<T: AsRawFd> AsRawFd for Box<T> { #[inline] diff --git a/library/std/src/os/hurd/mod.rs b/library/std/src/os/hurd/mod.rs index aee86c7f616..6cd50aeada1 100644 --- a/library/std/src/os/hurd/mod.rs +++ b/library/std/src/os/hurd/mod.rs @@ -1,6 +1,7 @@ //! Hurd-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index a4fa94e2b96..76f5f549dd2 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -485,6 +485,14 @@ impl<T: AsHandle + ?Sized> AsHandle for crate::rc::Rc<T> { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl<T: AsHandle + ?Sized> AsHandle for crate::rc::UniqueRc<T> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + (**self).as_handle() + } +} + #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl<T: AsHandle + ?Sized> AsHandle for Box<T> { #[inline] diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 272641ea6c7..c6d7bad9440 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -279,6 +279,14 @@ impl<T: AsSocket> AsSocket for crate::rc::Rc<T> { } } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl<T: AsSocket + ?Sized> AsSocket for crate::rc::UniqueRc<T> { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + (**self).as_socket() + } +} + #[stable(feature = "as_windows_ptrs", since = "1.71.0")] impl<T: AsSocket> AsSocket for Box<T> { #[inline] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 97f800dddaa..dca5ccca0c4 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -623,7 +623,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let mut fmt = fmt::Formatter::new(&mut s); + let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new()); let _err = fmt::Display::fmt(&inner, &mut fmt); s }) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 5b277a982ee..35e920ab344 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2327,7 +2327,9 @@ impl Path { // FIXME: Allow Redox prefixes self.has_root() || has_redox_scheme(self.as_u8_slice()) } else { - self.has_root() && (cfg!(any(unix, target_os = "wasi")) || self.prefix().is_some()) + self.has_root() + && (cfg!(any(unix, target_os = "hermit", target_os = "wasi")) + || self.prefix().is_some()) } } diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs index 891032e94a6..06f3fd9fdff 100644 --- a/library/std/src/pipe.rs +++ b/library/std/src/pipe.rs @@ -1,20 +1,66 @@ -//! Module for anonymous pipe +//! A cross-platform anonymous pipe. //! -//! ``` -//! #![feature(anonymous_pipe)] +//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on +//! Windows. +//! +//! # 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<()> { -//! let (reader, writer) = std::pipe::pipe()?; +//! # use std::process::Command; +//! # use std::io::{Read, Write}; +//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?; +//! let (mut pong_rx, pong_tx) = std::pipe::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 use crate::io; use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; /// Create anonymous pipe that is close-on-exec and blocking. +/// +/// # Examples +/// +/// See the [module-level](crate::pipe) documentation for examples. #[unstable(feature = "anonymous_pipe", issue = "127154")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { @@ -33,6 +79,58 @@ 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::pipe::pipe()?; + /// + /// // Write NUM_SLOT characters the 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) @@ -41,6 +139,38 @@ impl PipeReader { 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::pipe::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) diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs index e4731280ffe..22a364074c5 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -12,7 +12,8 @@ pub use crate::marker::{Send, Sized, Sync, Unpin}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[unstable(feature = "async_closure", issue = "62290")] +#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] +#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] #[doc(no_inline)] pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 0c610ba67e6..64349987fcf 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -25,6 +25,7 @@ //! //! # Prelude contents //! +//! The items included in the prelude depend on the edition of the crate. //! The first version of the prelude is used in Rust 2015 and Rust 2018, //! and lives in [`std::prelude::v1`]. //! [`std::prelude::rust_2015`] and [`std::prelude::rust_2018`] re-export this prelude. @@ -32,8 +33,9 @@ //! //! * <code>[std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}</code>, //! marker traits that indicate fundamental properties of types. -//! * <code>[std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}</code>, various -//! operations for both destructors and overloading `()`. +//! * <code>[std::ops]::{[Fn], [FnMut], [FnOnce]}</code>, and their analogous +//! async traits, <code>[std::ops]::{[AsyncFn], [AsyncFnMut], [AsyncFnOnce]}</code>. +//! * <code>[std::ops]::[Drop]</code>, for implementing destructors. //! * <code>[std::mem]::[drop]</code>, a convenience function for explicitly //! dropping a value. //! * <code>[std::mem]::{[size_of], [size_of_val]}</code>, to get the size of @@ -67,15 +69,21 @@ //! The prelude used in Rust 2021, [`std::prelude::rust_2021`], includes all of the above, //! and in addition re-exports: //! -//! * <code>[std::convert]::{[TryFrom], [TryInto]}</code>, +//! * <code>[std::convert]::{[TryFrom], [TryInto]}</code>. //! * <code>[std::iter]::[FromIterator]</code>. //! +//! The prelude used in Rust 2024, [`std::prelude::rust_2024`], includes all of the above, +//! and in addition re-exports: +//! +//! * <code>[std::future]::{[Future], [IntoFuture]}</code>. +//! //! [std::borrow]: crate::borrow //! [std::boxed]: crate::boxed //! [std::clone]: crate::clone //! [std::cmp]: crate::cmp //! [std::convert]: crate::convert //! [std::default]: crate::default +//! [std::future]: crate::future //! [std::iter]: crate::iter //! [std::marker]: crate::marker //! [std::mem]: crate::mem @@ -85,6 +93,7 @@ //! [`std::prelude::rust_2015`]: rust_2015 //! [`std::prelude::rust_2018`]: rust_2018 //! [`std::prelude::rust_2021`]: rust_2021 +//! [`std::prelude::rust_2024`]: rust_2024 //! [std::result]: crate::result //! [std::slice]: crate::slice //! [std::string]: crate::string @@ -94,6 +103,8 @@ //! [book-dtor]: ../../book/ch15-03-drop.html //! [book-enums]: ../../book/ch06-01-defining-an-enum.html //! [book-iter]: ../../book/ch13-02-iterators.html +//! [Future]: crate::future::Future +//! [IntoFuture]: crate::future::IntoFuture // No formatting: this file is nothing but re-exports, and their order is worth preserving. #![cfg_attr(rustfmt, rustfmt::skip)] @@ -158,12 +169,12 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[unstable(feature = "prelude_2024", issue = "121042")] +#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] pub use super::common::*; - #[unstable(feature = "prelude_2024", issue = "121042")] + #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index fe2aca031a2..e28c2090afe 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -4,10 +4,10 @@ mod tests; use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::ManuallyDrop; +use crate::mem::{self, ManuallyDrop}; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; -use crate::sync::{LockResult, TryLockError, TryLockResult, poison}; +use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; use crate::sys::sync as sys; /// A mutual exclusion primitive useful for protecting shared data @@ -273,6 +273,100 @@ impl<T> Mutex<T> { pub const fn new(t: T) -> Mutex<T> { Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } + + /// Returns the contained value by cloning it. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned().unwrap(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn get_cloned(&self) -> Result<T, PoisonError<()>> + where + T: Clone, + { + match self.lock() { + Ok(guard) => Ok((*guard).clone()), + Err(_) => Err(PoisonError::new(())), + } + } + + /// Sets the contained value. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error containing the provided `value` instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned().unwrap(), 7); + /// mutex.set(11).unwrap(); + /// assert_eq!(mutex.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn set(&self, value: T) -> Result<(), PoisonError<T>> { + if mem::needs_drop::<T>() { + // If the contained value has non-trivial destructor, we + // call that destructor after the lock being released. + self.replace(value).map(drop) + } else { + match self.lock() { + Ok(mut guard) => { + *guard = value; + + Ok(()) + } + Err(_) => Err(PoisonError::new(value)), + } + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Errors + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will return an error containing the provided `value` instead. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.replace(11).unwrap(), 7); + /// assert_eq!(mutex.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn replace(&self, value: T) -> LockResult<T> { + match self.lock() { + Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), + Err(_) => Err(PoisonError::new(value)), + } + } } impl<T: ?Sized> Mutex<T> { @@ -290,7 +384,8 @@ impl<T: ?Sized> Mutex<T> { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error once the mutex is acquired. + /// this call will return an error once the mutex is acquired. The acquired + /// mutex guard will be contained in the returned error. /// /// # Panics /// @@ -331,7 +426,8 @@ impl<T: ?Sized> Mutex<T> { /// /// If another user of this mutex panicked while holding the mutex, then /// this call will return the [`Poisoned`] error if the mutex would - /// otherwise be acquired. + /// otherwise be acquired. An acquired lock guard will be contained + /// in the returned error. /// /// If the mutex could not be acquired because it is already locked, then /// this call will return the [`WouldBlock`] error. @@ -438,7 +534,8 @@ impl<T: ?Sized> Mutex<T> { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. + /// this call will return an error containing the the underlying data + /// instead. /// /// # Examples /// @@ -465,7 +562,8 @@ impl<T: ?Sized> Mutex<T> { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error instead. + /// this call will return an error containing a mutable reference to the + /// underlying data instead. /// /// # Examples /// diff --git a/library/std/src/sync/mutex/tests.rs b/library/std/src/sync/mutex/tests.rs index 19ec096c593..395c8aada08 100644 --- a/library/std/src/sync/mutex/tests.rs +++ b/library/std/src/sync/mutex/tests.rs @@ -1,13 +1,34 @@ +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::thread; +use crate::{hint, mem, thread}; struct Packet<T>(Arc<(Mutex<T>, Condvar)>); #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::<NonCopy>()); + assert!(mem::needs_drop::<NonCopyNeedsDrop>()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + #[test] fn smoke() { let m = Mutex::new(()); @@ -57,6 +78,21 @@ fn try_lock() { *m.try_lock().unwrap() = (); } +fn new_poisoned_mutex<T>(value: T) -> Mutex<T> { + let mutex = Mutex::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = mutex.lock().unwrap(); + + panic!("test panic to poison mutex"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(mutex.is_poisoned()); + + mutex +} + #[test] fn test_into_inner() { let m = Mutex::new(NonCopy(10)); @@ -83,22 +119,32 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); + let m = new_poisoned_mutex(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { + match m.into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), } } #[test] +fn test_get_cloned() { + let m = Mutex::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_mutex(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] fn test_get_mut() { let mut m = Mutex::new(NonCopy(10)); *m.get_mut().unwrap() = NonCopy(20); @@ -107,22 +153,91 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let m = Arc::new(Mutex::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.lock().unwrap(); - panic!("test panic in inner thread to poison mutex"); - }) - .join(); + let mut m = new_poisoned_mutex(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { + match m.get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), } } #[test] +fn test_set() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] fn test_mutex_arc_condvar() { let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); let packet2 = Packet(packet.0.clone()); @@ -269,7 +384,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_unlocked_poison() { let lock = Mutex::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); }); @@ -282,7 +397,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -295,7 +410,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); @@ -309,7 +424,7 @@ fn panic_while_mapping_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.lock().unwrap(); let guard = MutexGuard::map::<(), _>(guard, |val| val); let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index da66a088e51..9eb900c2103 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -87,8 +87,8 @@ pub struct Guard { /// /// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock /// is held. The precise semantics for when a lock is poisoned is documented on -/// each lock, but once a lock is poisoned then all future acquisitions will -/// return this error. +/// each lock. For a lock in the poisoned state, unless the state is cleared manually, +/// all future acquisitions will return this error. /// /// # Examples /// @@ -118,7 +118,7 @@ pub struct Guard { /// [`RwLock`]: crate::sync::RwLock #[stable(feature = "rust1", since = "1.0.0")] pub struct PoisonError<T> { - guard: T, + data: T, #[cfg(not(panic = "unwind"))] _never: !, } @@ -147,14 +147,15 @@ pub enum TryLockError<T> { /// A type alias for the result of a lock method which can be poisoned. /// /// The [`Ok`] variant of this result indicates that the primitive was not -/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates +/// poisoned, and the operation result is contained within. The [`Err`] variant indicates /// that the primitive was poisoned. Note that the [`Err`] variant *also* carries -/// the associated guard, and it can be acquired through the [`into_inner`] -/// method. +/// an associated value assigned by the lock method, and it can be acquired through the +/// [`into_inner`] method. The semantics of the associated value depends on the corresponding +/// lock method. /// /// [`into_inner`]: PoisonError::into_inner #[stable(feature = "rust1", since = "1.0.0")] -pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>; +pub type LockResult<T> = Result<T, PoisonError<T>>; /// A type alias for the result of a nonblocking locking method. /// @@ -195,8 +196,8 @@ impl<T> PoisonError<T> { /// This method may panic if std was built with `panic="abort"`. #[cfg(panic = "unwind")] #[stable(feature = "sync_poison", since = "1.2.0")] - pub fn new(guard: T) -> PoisonError<T> { - PoisonError { guard } + pub fn new(data: T) -> PoisonError<T> { + PoisonError { data } } /// Creates a `PoisonError`. @@ -208,12 +209,12 @@ impl<T> PoisonError<T> { #[cfg(not(panic = "unwind"))] #[stable(feature = "sync_poison", since = "1.2.0")] #[track_caller] - pub fn new(_guard: T) -> PoisonError<T> { + pub fn new(_data: T) -> PoisonError<T> { panic!("PoisonError created in a libstd built with panic=\"abort\"") } /// Consumes this error indicating that a lock is poisoned, returning the - /// underlying guard to allow access regardless. + /// associated data. /// /// # Examples /// @@ -238,21 +239,21 @@ impl<T> PoisonError<T> { /// ``` #[stable(feature = "sync_poison", since = "1.2.0")] pub fn into_inner(self) -> T { - self.guard + self.data } /// Reaches into this error indicating that a lock is poisoned, returning a - /// reference to the underlying guard to allow access regardless. + /// reference to the associated data. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_ref(&self) -> &T { - &self.guard + &self.data } /// Reaches into this error indicating that a lock is poisoned, returning a - /// mutable reference to the underlying guard to allow access regardless. + /// mutable reference to the associated data. #[stable(feature = "sync_poison", since = "1.2.0")] pub fn get_mut(&mut self) -> &mut T { - &mut self.guard + &mut self.data } } @@ -322,6 +323,6 @@ where match result { Ok(t) => Ok(f(t)), #[cfg(panic = "unwind")] - Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))), + Err(PoisonError { data }) => Err(PoisonError::new(f(data))), } } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index d55d1c80dca..1519baf99a8 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -4,7 +4,7 @@ mod tests; use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; -use crate::mem::{ManuallyDrop, forget}; +use crate::mem::{self, ManuallyDrop, forget}; use crate::ops::{Deref, DerefMut}; use crate::ptr::NonNull; use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison}; @@ -224,6 +224,103 @@ impl<T> RwLock<T> { pub const fn new(t: T) -> RwLock<T> { RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) } } + + /// Returns the contained value by cloning it. + /// + /// # Errors + /// + /// This function will return an error if the `RwLock` is poisoned. An + /// `RwLock` is poisoned whenever a writer panics while holding an exclusive + /// lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned().unwrap(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn get_cloned(&self) -> Result<T, PoisonError<()>> + where + T: Clone, + { + match self.read() { + Ok(guard) => Ok((*guard).clone()), + Err(_) => Err(PoisonError::new(())), + } + } + + /// Sets the contained value. + /// + /// # Errors + /// + /// This function will return an error containing the provided `value` if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned().unwrap(), 7); + /// lock.set(11).unwrap(); + /// assert_eq!(lock.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn set(&self, value: T) -> Result<(), PoisonError<T>> { + if mem::needs_drop::<T>() { + // If the contained value has non-trivial destructor, we + // call that destructor after the lock being released. + self.replace(value).map(drop) + } else { + match self.write() { + Ok(mut guard) => { + *guard = value; + + Ok(()) + } + Err(_) => Err(PoisonError::new(value)), + } + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Errors + /// + /// This function will return an error containing the provided `value` if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. + /// + /// # Examples + /// + /// ``` + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.replace(11).unwrap(), 7); + /// assert_eq!(lock.get_cloned().unwrap(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + pub fn replace(&self, value: T) -> LockResult<T> { + match self.write() { + Ok(mut guard) => Ok(mem::replace(&mut *guard, value)), + Err(_) => Err(PoisonError::new(value)), + } + } } impl<T: ?Sized> RwLock<T> { @@ -244,7 +341,8 @@ impl<T: ?Sized> RwLock<T> { /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive /// lock. The failure will occur immediately after the lock has been - /// acquired. + /// acquired. The acquired lock guard will be contained in the returned + /// error. /// /// # Panics /// @@ -292,7 +390,8 @@ impl<T: ?Sized> RwLock<T> { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. + /// have otherwise been acquired. An acquired lock guard will be contained + /// in the returned error. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -337,7 +436,8 @@ impl<T: ?Sized> RwLock<T> { /// /// This function will return an error if the `RwLock` is poisoned. An /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will be returned when the lock is acquired. + /// lock. An error will be returned when the lock is acquired. The acquired + /// lock guard will be contained in the returned error. /// /// # Panics /// @@ -380,7 +480,8 @@ impl<T: ?Sized> RwLock<T> { /// This function will return the [`Poisoned`] error if the `RwLock` is /// poisoned. An `RwLock` is poisoned whenever a writer panics while holding /// an exclusive lock. `Poisoned` will only be returned if the lock would - /// have otherwise been acquired. + /// have otherwise been acquired. An acquired lock guard will be contained + /// in the returned error. /// /// This function will return the [`WouldBlock`] error if the `RwLock` could /// not be acquired because it was already locked exclusively. @@ -481,10 +582,10 @@ impl<T: ?Sized> RwLock<T> { /// /// # Errors /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will only be returned if the lock would have otherwise - /// been acquired. + /// This function will return an error containing the underlying data if + /// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer + /// panics while holding an exclusive lock. An error will only be returned + /// if the lock would have otherwise been acquired. /// /// # Examples /// @@ -514,10 +615,11 @@ impl<T: ?Sized> RwLock<T> { /// /// # Errors /// - /// This function will return an error if the `RwLock` is poisoned. An - /// `RwLock` is poisoned whenever a writer panics while holding an exclusive - /// lock. An error will only be returned if the lock would have otherwise - /// been acquired. + /// This function will return an error containing a mutable reference to + /// the underlying data if the `RwLock` is poisoned. An `RwLock` is + /// poisoned whenever a writer panics while holding an exclusive lock. + /// An error will only be returned if the lock would have otherwise been + /// acquired. /// /// # Examples /// diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs index 48d442921f7..057c2f1a5d7 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/src/sync/rwlock/tests.rs @@ -1,16 +1,37 @@ 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::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use crate::thread; +use crate::{hint, mem, thread}; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::<NonCopy>()); + assert!(mem::needs_drop::<NonCopyNeedsDrop>()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + #[test] fn smoke() { let l = RwLock::new(()); @@ -255,6 +276,21 @@ fn test_rwlock_try_write() { drop(mapped_read_guard); } +fn new_poisoned_rwlock<T>(value: T) -> RwLock<T> { + let lock = RwLock::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = lock.write().unwrap(); + + panic!("test panic to poison RwLock"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(lock.is_poisoned()); + + lock +} + #[test] fn test_into_inner() { let m = RwLock::new(NonCopy(10)); @@ -281,22 +317,32 @@ fn test_into_inner_drop() { #[test] fn test_into_inner_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); + let m = new_poisoned_rwlock(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().into_inner() { + match m.into_inner() { Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"), } } #[test] +fn test_get_cloned() { + let m = RwLock::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_rwlock(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"), + } +} + +#[test] fn test_get_mut() { let mut m = RwLock::new(NonCopy(10)); *m.get_mut().unwrap() = NonCopy(20); @@ -305,22 +351,91 @@ fn test_get_mut() { #[test] fn test_get_mut_poison() { - let m = Arc::new(RwLock::new(NonCopy(10))); - let m2 = m.clone(); - let _ = thread::spawn(move || { - let _lock = m2.write().unwrap(); - panic!("test panic in inner thread to poison RwLock"); - }) - .join(); + let mut m = new_poisoned_rwlock(NonCopy(10)); - assert!(m.is_poisoned()); - match Arc::try_unwrap(m).unwrap().get_mut() { + match m.get_mut() { Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"), } } #[test] +fn test_set() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] fn test_read_guard_covariance() { fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} let j: i32 = 5; @@ -370,7 +485,7 @@ fn test_mapping_mapped_guard() { fn panic_while_mapping_read_unlocked_no_poison() { let lock = RwLock::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!()); }); @@ -385,7 +500,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -400,7 +515,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!()); @@ -416,7 +531,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { } } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.read().unwrap(); let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); @@ -439,7 +554,7 @@ fn panic_while_mapping_read_unlocked_no_poison() { fn panic_while_mapping_write_unlocked_poison() { let lock = RwLock::new(()); - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!()); }); @@ -452,7 +567,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); }); @@ -467,7 +582,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!()); @@ -483,7 +598,7 @@ fn panic_while_mapping_write_unlocked_poison() { Err(TryLockError::Poisoned(_)) => {} } - let _ = crate::panic::catch_unwind(|| { + let _ = panic::catch_unwind(|| { let guard = lock.write().unwrap(); let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 4d939e175cf..efa6a896dad 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -58,8 +58,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: 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; + // If we're using a short backtrace, ignore all frames until we're told to start printing. + let mut print = print_fmt != PrintFmt::Short; set_image_base(); // SAFETY: we roll our own locking in this town unsafe { @@ -72,27 +72,25 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { hit = true; - // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` - // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be - // called before the panic hook, so we won't ignore any frames if there is no - // invoke of `__rust_begin_short_backtrace`. + // `__rust_end_short_backtrace` means we are done hiding symbols + // for now. Print until we see `__rust_begin_short_backtrace`. if print_fmt == PrintFmt::Short { if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if start && sym.contains("__rust_begin_short_backtrace") { - start = false; + if sym.contains("__rust_end_short_backtrace") { + print = true; return; } - if sym.contains("__rust_end_short_backtrace") { - start = true; + if print && sym.contains("__rust_begin_short_backtrace") { + print = false; return; } - if !start { + if !print { omitted_count += 1; } } } - if start { + if print { if omitted_count > 0 { debug_assert!(print_fmt == PrintFmt::Short); // only print the message between the middle of frames @@ -112,7 +110,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: }); #[cfg(target_os = "nto")] if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { - if !hit && start { + if !hit && print { use crate::backtrace_rs::SymbolName; res = bt_fmt.frame().print_raw( frame.ip(), @@ -123,7 +121,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: } return false; } - if !hit && start { + if !hit && print { res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 11862a07608..88fc4068719 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -3,7 +3,7 @@ use super::hermit_abi::{ self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct, }; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; @@ -204,7 +204,7 @@ impl Iterator for ReadDir { // the size of dirent64. The file name is always a C string and terminated by `\0`. // Consequently, we are able to ignore the last byte. let name_bytes = - unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const i8).to_bytes() }; + unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() }; let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, @@ -445,7 +445,7 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ()) }) } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index f03fca60344..d833c9d632c 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -85,7 +85,7 @@ pub unsafe extern "C" fn runtime_entry( } // initialize environment - os::init_environment(env as *const *const i8); + os::init_environment(env); let result = unsafe { main(argc as isize, argv) }; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index f8ea80afa43..791cdb1e57e 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -3,7 +3,7 @@ use core::slice::memchr; use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString, c_char}; use crate::marker::PhantomData; use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; @@ -70,7 +70,7 @@ pub fn current_exe() -> io::Result<PathBuf> { static ENV: Mutex<Option<HashMap<OsString, OsString>>> = Mutex::new(None); -pub fn init_environment(env: *const *const i8) { +pub fn init_environment(env: *const *const c_char) { let mut guard = ENV.lock().unwrap(); let map = guard.insert(HashMap::new()); diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 2bf2e2ceb31..a9900f55b19 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -71,7 +71,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => FilesystemQuotaExceeded, + libc::EDQUOT => QuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 8eaa50d7f81..3cc1cae8d00 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -254,7 +254,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { libc::ECONNREFUSED => ConnectionRefused, libc::ECONNRESET => ConnectionReset, libc::EDEADLK => Deadlock, - libc::EDQUOT => FilesystemQuotaExceeded, + libc::EDQUOT => QuotaExceeded, libc::EEXIST => AlreadyExists, libc::EFBIG => FileTooLarge, libc::EHOSTUNREACH => HostUnreachable, diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index d140607869c..d73b9fd5eb8 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -81,6 +81,7 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", target_os = "nto", + target_os = "solaris", ))] { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 0667eb90101..7779d2b97d7 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -27,10 +27,27 @@ pub struct FileAttr { pub struct ReadDir { inner: Arc<ReadDirInner>, - cookie: Option<wasi::Dircookie>, - buf: Vec<u8>, - offset: usize, - cap: usize, + state: ReadDirState, +} + +enum ReadDirState { + /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. + FillBuffer { + next_read_offset: wasi::Dircookie, + buf: Vec<u8>, + }, + ProcessEntry { + buf: Vec<u8>, + next_read_offset: Option<wasi::Dircookie>, + offset: usize, + }, + /// There is no more data to get in [`Self::FillBuffer`]; keep returning + /// entries via ProcessEntry until `buf` is exhausted. + RunUntilExhaustion { + buf: Vec<u8>, + offset: usize, + }, + Done, } struct ReadDirInner { @@ -147,11 +164,8 @@ impl FileType { impl ReadDir { fn new(dir: File, root: PathBuf) -> ReadDir { ReadDir { - cookie: Some(0), - buf: vec![0; 128], - offset: 0, - cap: 0, inner: Arc::new(ReadDirInner { dir, root }), + state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, } } } @@ -162,78 +176,99 @@ impl fmt::Debug for ReadDir { } } +impl core::iter::FusedIterator for ReadDir {} + impl Iterator for ReadDir { type Item = io::Result<DirEntry>; fn next(&mut self) -> Option<io::Result<DirEntry>> { - loop { - // If we've reached the capacity of our buffer then we need to read - // some more from the OS, otherwise we pick up at our old offset. - let offset = if self.offset == self.cap { - let cookie = self.cookie.take()?; - match self.inner.dir.fd.readdir(&mut self.buf, cookie) { - Ok(bytes) => self.cap = bytes, - Err(e) => return Some(Err(e)), - } - self.offset = 0; - self.cookie = Some(cookie); - - // If we didn't actually read anything, this is in theory the - // end of the directory. - if self.cap == 0 { - self.cookie = None; - return None; - } - - 0 - } else { - self.offset - }; - let data = &self.buf[offset..self.cap]; - - // If we're not able to read a directory entry then that means it - // must have been truncated at the end of the buffer, so reset our - // offset so we can go back and reread into the buffer, picking up - // where we last left off. - let dirent_size = mem::size_of::<wasi::Dirent>(); - if data.len() < dirent_size { - assert!(self.cookie.is_some()); - assert!(self.buf.len() >= dirent_size); - self.offset = self.cap; - continue; - } - let (dirent, data) = data.split_at(dirent_size); - let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. Note that if our offset is 0 that means the file name - // is massive and we need a bigger buffer. - if data.len() < dirent.d_namlen as usize { - if offset == 0 { - let amt_to_add = self.buf.capacity(); - self.buf.extend(iter::repeat(0).take(amt_to_add)); + match &mut self.state { + ReadDirState::FillBuffer { next_read_offset, ref mut buf } => { + let result = self.inner.dir.fd.readdir(buf, *next_read_offset); + match result { + Ok(read_bytes) => { + if read_bytes < buf.len() { + buf.truncate(read_bytes); + self.state = + ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; + } else { + debug_assert_eq!(read_bytes, buf.len()); + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: 0, + next_read_offset: Some(*next_read_offset), + }; + } + self.next() + } + Err(e) => { + self.state = ReadDirState::Done; + return Some(Err(e)); + } } - assert!(self.cookie.is_some()); - self.offset = self.cap; - continue; } - self.cookie = Some(dirent.d_next); - self.offset = offset + dirent_size + dirent.d_namlen as usize; + ReadDirState::ProcessEntry { ref mut buf, next_read_offset, offset } => { + let contents = &buf[*offset..]; + const DIRENT_SIZE: usize = crate::mem::size_of::<wasi::Dirent>(); + if contents.len() >= DIRENT_SIZE { + let (dirent, data) = contents.split_at(DIRENT_SIZE); + let dirent = + unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; + // If the file name was truncated, then we need to reinvoke + // `readdir` so we truncate our buffer to start over and reread this + // descriptor. + if data.len() < dirent.d_namlen as usize { + if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { + buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); + } + if let Some(next_read_offset) = *next_read_offset { + self.state = + ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + + return self.next(); + } + next_read_offset.as_mut().map(|cookie| { + *cookie = dirent.d_next; + }); + *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize; - let name = &data[..(dirent.d_namlen as usize)]; + let name = &data[..(dirent.d_namlen as usize)]; + + // These names are skipped on all other platforms, so let's skip + // them here too + if name == b"." || name == b".." { + return self.next(); + } - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - continue; + return Some(Ok(DirEntry { + meta: dirent, + name: name.to_vec(), + inner: self.inner.clone(), + })); + } else if let Some(next_read_offset) = *next_read_offset { + self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; + } else { + self.state = ReadDirState::Done; + } + self.next() } + ReadDirState::RunUntilExhaustion { buf, offset } => { + if *offset >= buf.len() { + self.state = ReadDirState::Done; + } else { + self.state = ReadDirState::ProcessEntry { + buf: mem::take(buf), + offset: *offset, + next_read_offset: None, + }; + } - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); + self.next() + } + ReadDirState::Done => None, } } } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index d66ff15e10b..88e6def7a75 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -113,7 +113,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, c::ERROR_SEEK_ON_DEVICE => return NotSeekable, - c::ERROR_DISK_QUOTA_EXCEEDED => return FilesystemQuotaExceeded, + c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, c::ERROR_FILE_TOO_LARGE => return FileTooLarge, c::ERROR_BUSY => return ResourceBusy, c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, @@ -138,7 +138,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { c::WSAEHOSTUNREACH => HostUnreachable, c::WSAENETDOWN => NetworkDown, c::WSAENETUNREACH => NetworkUnreachable, - c::WSAEDQUOT => FilesystemQuotaExceeded, + c::WSAEDQUOT => QuotaExceeded, _ => Uncategorized, } diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs index 28e48a750b9..b4b58b34706 100644 --- a/library/std/src/sys/thread_local/key/unix.rs +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -1,5 +1,25 @@ use crate::mem; +// For WASI add a few symbols not in upstream `libc` just yet. +#[cfg(all(target_os = "wasi", target_env = "p1", target_feature = "atomics"))] +mod libc { + use crate::ffi; + + #[allow(non_camel_case_types)] + pub type pthread_key_t = ffi::c_uint; + + extern "C" { + pub fn pthread_key_create( + key: *mut pthread_key_t, + destructor: unsafe extern "C" fn(*mut ffi::c_void), + ) -> ffi::c_int; + #[allow(dead_code)] + pub fn pthread_getspecific(key: pthread_key_t) -> *mut ffi::c_void; + pub fn pthread_setspecific(key: pthread_key_t, value: *const ffi::c_void) -> ffi::c_int; + pub fn pthread_key_delete(key: pthread_key_t) -> ffi::c_int; + } +} + pub type Key = libc::pthread_key_t; #[inline] diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 31d3b439060..f0a13323ec9 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -86,7 +86,9 @@ pub(crate) mod guard { mod windows; pub(crate) use windows::enable; } else if #[cfg(any( - target_family = "wasm", + all(target_family = "wasm", not( + all(target_os = "wasi", target_env = "p1", target_feature = "atomics") + )), target_os = "uefi", target_os = "zkvm", ))] { @@ -135,6 +137,7 @@ pub(crate) mod key { target_family = "unix", ), target_os = "teeos", + all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), ))] { mod racy; mod unix; diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 56cf438bd9b..2313f4b5beb 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -12,7 +12,7 @@ use crate::cell::{Cell, RefCell}; use crate::error::Error; use crate::fmt; -/// A thread local storage key which owns its contents. +/// A thread local storage (TLS) key which owns its contents. /// /// This key uses the fastest possible implementation available to it for the /// target platform. It is instantiated with the [`thread_local!`] macro and the |
