From a3208108b0b2b98004dac023aaeb794c1ec9149a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Fern=C3=A1ndez=20Serrata?= <76864299+Rudxain@users.noreply.github.com> Date: Fri, 4 Jul 2025 19:10:39 -0400 Subject: [PATCH 1/9] fix(lib-std-fs): handle `usize` overflow in `read` & `read_to_string` --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 72ad7c244eeba..9fbfd92eaf883 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -304,7 +304,7 @@ pub struct DirBuilder { pub fn read>(path: P) -> io::Result> { fn inner(path: &Path) -> io::Result> { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len() as usize).ok(); + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); let mut bytes = Vec::try_with_capacity(size.unwrap_or(0))?; io::default_read_to_end(&mut file, &mut bytes, size)?; Ok(bytes) @@ -346,7 +346,7 @@ pub fn read>(path: P) -> io::Result> { pub fn read_to_string>(path: P) -> io::Result { fn inner(path: &Path) -> io::Result { let mut file = File::open(path)?; - let size = file.metadata().map(|m| m.len() as usize).ok(); + let size = file.metadata().map(|m| usize::try_from(m.len()).unwrap_or(usize::MAX)).ok(); let mut string = String::new(); string.try_reserve_exact(size.unwrap_or(0))?; io::default_read_to_string(&mut file, &mut string, size)?; From 489940ee1d24a0a2bc388b465f171da9ab0cb16b Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 15 Aug 2025 16:42:21 +0000 Subject: [PATCH 2/9] stabilize array repeat --- library/core/src/array/mod.rs | 4 +--- tests/codegen-llvm/array-repeat.rs | 1 - tests/codegen-llvm/iter-repeat-n-trivial-drop.rs | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index b3a498570f95a..e994fa00a2328 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -41,8 +41,6 @@ pub use iter::IntoIter; /// /// Creating multiple copies of a `String`: /// ```rust -/// #![feature(array_repeat)] -/// /// use std::array; /// /// let string = "Hello there!".to_string(); @@ -50,7 +48,7 @@ pub use iter::IntoIter; /// assert_eq!(strings, ["Hello there!", "Hello there!"]); /// ``` #[inline] -#[unstable(feature = "array_repeat", issue = "126695")] +#[stable(feature = "array_repeat", since = "CURRENT_RUSTC_VERSION")] pub fn repeat(val: T) -> [T; N] { from_trusted_iterator(repeat_n(val, N)) } diff --git a/tests/codegen-llvm/array-repeat.rs b/tests/codegen-llvm/array-repeat.rs index 4c755df939034..1c45341d764cd 100644 --- a/tests/codegen-llvm/array-repeat.rs +++ b/tests/codegen-llvm/array-repeat.rs @@ -1,7 +1,6 @@ //@ compile-flags: -Copt-level=3 #![crate_type = "lib"] -#![feature(array_repeat)] use std::array::repeat; diff --git a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs index 2817353032437..6f34097842800 100644 --- a/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen-llvm/iter-repeat-n-trivial-drop.rs @@ -4,7 +4,6 @@ #![crate_type = "lib"] #![feature(iter_repeat_n)] -#![feature(array_repeat)] #[derive(Clone)] pub struct NotCopy(u16); From eaf7fd2fed70cac9ca6d75d011e1d11554c8ef5b Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 23 Aug 2025 09:14:51 -0400 Subject: [PATCH 3/9] add `nonpoison::condvar` implementation Adds the equivalent `nonpoison` types to the `poison::condvar` module. These types and implementations are gated under the `nonpoison_condvar` feature gate. Signed-off-by: Connor Tsui --- library/std/src/sync/nonpoison.rs | 3 + library/std/src/sync/nonpoison/condvar.rs | 516 ++++++++++++++++++++++ library/std/src/sync/nonpoison/mutex.rs | 7 +- 3 files changed, 524 insertions(+), 2 deletions(-) create mode 100644 library/std/src/sync/nonpoison/condvar.rs diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs index b3ae376e70d55..96ecf7084fa94 100644 --- a/library/std/src/sync/nonpoison.rs +++ b/library/std/src/sync/nonpoison.rs @@ -29,6 +29,8 @@ impl fmt::Display for WouldBlock { } } +#[unstable(feature = "nonpoison_condvar", issue = "134645")] +pub use self::condvar::{Condvar, WaitTimeoutResult}; #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub use self::mutex::MappedMutexGuard; #[unstable(feature = "nonpoison_mutex", issue = "134645")] @@ -38,5 +40,6 @@ pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[unstable(feature = "nonpoison_rwlock", issue = "134645")] pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +mod condvar; mod mutex; mod rwlock; diff --git a/library/std/src/sync/nonpoison/condvar.rs b/library/std/src/sync/nonpoison/condvar.rs new file mode 100644 index 0000000000000..9744c87d9cc1e --- /dev/null +++ b/library/std/src/sync/nonpoison/condvar.rs @@ -0,0 +1,516 @@ +use crate::fmt; +use crate::sync::nonpoison::{MutexGuard, mutex}; +use crate::sys::sync as sys; +use crate::time::{Duration, Instant}; + +/// A type indicating whether a timed wait on a condition variable returned +/// due to a time out or not. +/// +/// It is returned by the [`wait_timeout`] method. +/// +/// [`wait_timeout`]: Condvar::wait_timeout +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[unstable(feature = "nonpoison_condvar", issue = "134645")] +pub struct WaitTimeoutResult(bool); + +// FIXME(nonpoison_condvar) this type is duplicated in `poison`. How do we share types that are +// poison-agnostic? +impl WaitTimeoutResult { + /// Returns `true` if the wait was known to have timed out. + /// + /// # Examples + /// + /// 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 10 millisecond timeout on the condvar + /// and will leave the loop upon timeout. + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// # let handle = + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// + /// // Let's wait 20 milliseconds before notifying the condvar. + /// thread::sleep(Duration::from_millis(20)); + /// + /// let mut started = lock.lock(); + /// // We update the boolean value. + /// *started = true; + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// loop { + /// // Let's put a timeout on the condvar's wait. + /// let result = cvar.wait_timeout(lock.lock(), Duration::from_millis(10)); + /// // 10 milliseconds have passed. + /// if result.1.timed_out() { + /// // timed out now and we can leave. + /// break + /// } + /// } + /// # // Prevent leaks for Miri. + /// # let _ = handle.join(); + /// ``` + #[must_use] + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn timed_out(&self) -> bool { + self.0 + } +} + +/// A Condition Variable +/// +/// For more information about condition variables, check out the documentation for the poisoning +/// variant of this type at [`poison::Condvar`]. +/// +/// # Examples +/// +/// Note that this `Condvar` does **not** propagate information about threads that panic while +/// holding a lock. If you need this functionality, see [`poison::Mutex`] and [`poison::Condvar`]. +/// +/// ``` +/// #![feature(nonpoison_mutex)] +/// #![feature(nonpoison_condvar)] +/// +/// use std::sync::nonpoison::{Mutex, Condvar}; +/// use std::sync::Arc; +/// use std::thread; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = Arc::clone(&pair); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start. +/// thread::spawn(move || { +/// let (lock, cvar) = &*pair2; +/// let mut started = lock.lock(); +/// *started = true; +/// // We notify the condvar that the value has changed. +/// cvar.notify_one(); +/// }); +/// +/// // Wait for the thread to start up. +/// let (lock, cvar) = &*pair; +/// let mut started = lock.lock(); +/// while !*started { +/// started = cvar.wait(started); +/// } +/// ``` +/// +/// [`poison::Mutex`]: crate::sync::poison::Mutex +/// [`poison::Condvar`]: crate::sync::poison::Condvar +#[unstable(feature = "nonpoison_condvar", issue = "134645")] +pub struct Condvar { + inner: sys::Condvar, +} + +impl Condvar { + /// Creates a new condition variable which is ready to be waited on and + /// notified. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Condvar; + /// + /// let condvar = Condvar::new(); + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + #[must_use] + #[inline] + pub const fn new() -> Condvar { + Condvar { inner: sys::Condvar::new() } + } + + /// Blocks the current thread until this condition variable receives a + /// notification. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// Note that this function is susceptible to spurious wakeups. Condition + /// variables normally have a boolean predicate associated with them, and + /// the predicate must always be checked each time this function returns to + /// protect against spurious wakeups. + /// + /// # Panics + /// + /// This function may [`panic!`] if it is used with more than one mutex + /// over time. + /// + /// [`notify_one`]: Self::notify_one + /// [`notify_all`]: Self::notify_all + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started); + /// } + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + unsafe { + let lock = mutex::guard_lock(&guard); + self.inner.wait(lock); + } + guard + } + + /// Blocks the current thread until the provided condition becomes false. + /// + /// `condition` is checked immediately; if not met (returns `true`), this + /// will [`wait`] for the next notification then check again. This repeats + /// until `condition` returns `false`, in which case this function returns. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls + /// to [`notify_one`] or [`notify_all`] which happen logically after the + /// mutex is unlocked are candidates to wake this thread up. When this + /// function call returns, the lock specified will have been re-acquired. + /// + /// [`wait`]: Self::wait + /// [`notify_one`]: Self::notify_one + /// [`notify_all`]: Self::notify_all + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(true), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut pending = lock.lock(); + /// *pending = false; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// // As long as the value inside the `Mutex` is `true`, we wait. + /// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending }); + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn wait_while<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + mut condition: F, + ) -> MutexGuard<'a, T> + where + F: FnMut(&mut T) -> bool, + { + while condition(&mut *guard) { + guard = self.wait(guard); + } + guard + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait`] except that + /// the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that might not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. This function is susceptible to spurious wakeups. + /// Condition variables normally have a boolean predicate associated with + /// them, and the predicate must always be checked each time this function + /// returns to protect against spurious wakeups. Additionally, it is + /// typically desirable for the timeout to not exceed some duration in + /// spite of spurious wakes, thus the sleep-duration is decremented by the + /// amount slept. Alternatively, use the `wait_timeout_while` method + /// to wait with a timeout while a predicate is true. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed. + /// + /// Like [`wait`], the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait`]: Self::wait + /// [`wait_timeout_while`]: Self::wait_timeout_while + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock(); + /// // as long as the value inside the `Mutex` is `false`, we wait + /// loop { + /// let result = cvar.wait_timeout(started, Duration::from_millis(10)); + /// // 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. + /// break + /// } + /// } + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn wait_timeout<'a, T>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) { + let success = unsafe { + let lock = mutex::guard_lock(&guard); + self.inner.wait_timeout(lock, dur) + }; + (guard, WaitTimeoutResult(!success)) + } + + /// Waits on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to [`wait_while`] except + /// that the thread will be blocked for roughly no longer than `dur`. This + /// method should not be used for precise timing due to anomalies such as + /// preemption or platform differences that might not cause the maximum + /// amount of time waited to be precisely `dur`. + /// + /// Note that the best effort is made to ensure that the time waited is + /// measured with a monotonic clock, and not affected by the changes made to + /// the system time. + /// + /// The returned [`WaitTimeoutResult`] value indicates if the timeout is + /// known to have elapsed without the condition being met. + /// + /// Like [`wait_while`], the lock specified will be re-acquired when this + /// function returns, regardless of whether the timeout elapsed or not. + /// + /// [`wait_while`]: Self::wait_while + /// [`wait_timeout`]: Self::wait_timeout + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(true), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut pending = lock.lock(); + /// *pending = false; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // wait for the thread to start up + /// let (lock, cvar) = &*pair; + /// let result = cvar.wait_timeout_while( + /// lock.lock(), + /// Duration::from_millis(100), + /// |&mut pending| pending, + /// ); + /// if result.1.timed_out() { + /// // timed-out without the condition ever evaluating to false. + /// } + /// // access the locked mutex via result.0 + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn wait_timeout_while<'a, T, F>( + &self, + mut guard: MutexGuard<'a, T>, + dur: Duration, + mut condition: F, + ) -> (MutexGuard<'a, T>, WaitTimeoutResult) + where + F: FnMut(&mut T) -> bool, + { + let start = Instant::now(); + loop { + if !condition(&mut *guard) { + return (guard, WaitTimeoutResult(false)); + } + let timeout = match dur.checked_sub(start.elapsed()) { + Some(timeout) => timeout, + None => return (guard, WaitTimeoutResult(true)), + }; + guard = self.wait_timeout(guard, timeout).0; + } + } + + /// Wakes up one blocked thread on this condvar. + /// + /// If there is a blocked thread on this condition variable, then it will + /// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to + /// `notify_one` are not buffered in any way. + /// + /// To wake up all threads, see [`notify_all`]. + /// + /// [`wait`]: Self::wait + /// [`wait_timeout`]: Self::wait_timeout + /// [`notify_all`]: Self::notify_all + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started); + /// } + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn notify_one(&self) { + self.inner.notify_one() + } + + /// Wakes up all blocked threads on this condvar. + /// + /// This method will ensure that any current waiters on the condition + /// variable are awoken. Calls to `notify_all()` are not buffered in any + /// way. + /// + /// To wake up only one thread, see [`notify_one`]. + /// + /// [`notify_one`]: Self::notify_one + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(nonpoison_condvar)] + /// + /// use std::sync::nonpoison::{Mutex, Condvar}; + /// use std::sync::Arc; + /// use std::thread; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// let mut started = lock.lock(); + /// *started = true; + /// // We notify the condvar that the value has changed. + /// cvar.notify_all(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// let mut started = lock.lock(); + /// // As long as the value inside the `Mutex` is `false`, we wait. + /// while !*started { + /// started = cvar.wait(started); + /// } + /// ``` + #[unstable(feature = "nonpoison_condvar", issue = "134645")] + pub fn notify_all(&self) { + self.inner.notify_all() + } +} + +#[unstable(feature = "nonpoison_condvar", issue = "134645")] +impl fmt::Debug for Condvar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Condvar").finish_non_exhaustive() + } +} + +#[unstable(feature = "nonpoison_condvar", issue = "134645")] +impl Default for Condvar { + /// Creates a `Condvar` which is ready to be waited on and notified. + fn default() -> Condvar { + Condvar::new() + } +} diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index fd1e671d7a3d3..f8af081e79d95 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -114,7 +114,6 @@ impl !Send for MutexGuard<'_, T> {} #[unstable(feature = "nonpoison_mutex", issue = "134645")] unsafe impl Sync for MutexGuard<'_, T> {} -// FIXME(nonpoison_condvar): Use this link instead: [`Condvar`]: crate::sync::nonpoison::Condvar /// An RAII mutex guard returned by `MutexGuard::map`, which can point to a /// subfield of the protected data. When this structure is dropped (falls out /// of scope), the lock will be unlocked. @@ -131,7 +130,7 @@ unsafe impl Sync for MutexGuard<'_, T> {} /// /// [`map`]: MutexGuard::map /// [`filter_map`]: MutexGuard::filter_map -/// [`Condvar`]: crate::sync::Condvar +/// [`Condvar`]: crate::sync::nonpoison::Condvar #[must_use = "if unused the Mutex will immediately unlock"] #[must_not_suspend = "holding a MappedMutexGuard across suspend \ points can cause deadlocks, delays, \ @@ -458,6 +457,10 @@ impl fmt::Display for MutexGuard<'_, T> { } } +pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { + &guard.lock.inner +} + impl<'a, T: ?Sized> MutexGuard<'a, T> { /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. /// an enum variant. From b2380d2bcc429b1ac8d800bc2fdce90de81d1454 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 23 Aug 2025 09:15:33 -0400 Subject: [PATCH 4/9] add nonpoison and poison condvar tests Adds tests for the `nonpoison::Mutex` variant by using a macro to duplicate the existing `poison` tests. Note that all of the tests here are adapted from the existing `poison` tests. Also steals the `test_mutex_arc_condvar` test from `mutex.rs`. Signed-off-by: Connor Tsui --- library/std/tests/sync/condvar.rs | 401 ++++++++++++++++++------------ library/std/tests/sync/lib.rs | 1 + library/std/tests/sync/mutex.rs | 34 --- 3 files changed, 241 insertions(+), 195 deletions(-) diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 834de6bb1c295..1d712a643003a 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -1,190 +1,269 @@ +use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::channel; -use std::sync::{Arc, Condvar, Mutex}; use std::thread; use std::time::Duration; -#[test] -fn smoke() { - let c = Condvar::new(); - c.notify_one(); - c.notify_all(); -} +use super::nonpoison_and_poison_unwrap_test; + +nonpoison_and_poison_unwrap_test!( + name: smoke, + test_body: { + use locks::Condvar; + + let c = Condvar::new(); + c.notify_one(); + c.notify_all(); + } +); -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn notify_one() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - c2.notify_one(); - }); - let g = c.wait(g).unwrap(); - drop(g); -} - -#[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn notify_all() { - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock().unwrap(); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cnt = cond.wait(cnt).unwrap(); - } - tx.send(()).unwrap(); +nonpoison_and_poison_unwrap_test!( + name: notify_one, + test_body: { + use locks::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let g = maybe_unwrap(m.lock()); + let _t = thread::spawn(move || { + let _g = maybe_unwrap(m2.lock()); + c2.notify_one(); }); + let g = maybe_unwrap(c.wait(g)); + drop(g); } - drop(tx); +); - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = lock.lock().unwrap(); - *cnt = 0; - cond.notify_all(); - drop(cnt); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +nonpoison_and_poison_unwrap_test!( + name: notify_all, + test_body: { + use locks::{Condvar, Mutex}; + + const N: usize = 10; - for _ in 0..N { + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = maybe_unwrap(lock.lock()); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cnt = maybe_unwrap(cond.wait(cnt)); + } + tx.send(()).unwrap(); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; rx.recv().unwrap(); + let mut cnt = maybe_unwrap(lock.lock()); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in 0..N { + rx.recv().unwrap(); + } } -} +); -#[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_while() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair2 = pair.clone(); - - // Inside of our lock, spawn a new thread, and then wait for it to start. - thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; - let mut started = lock.lock().unwrap(); - *started = true; - // We notify the condvar that the value has changed. - cvar.notify_one(); - }); - - // Wait for the thread to start up. - let &(ref lock, ref cvar) = &*pair; - let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started); - assert!(*guard.unwrap()); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = m.lock().unwrap(); - let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not timeout - if !no_timeout.timed_out() { - continue; +nonpoison_and_poison_unwrap_test!( + name: test_mutex_arc_condvar, + test_body: { + use locks::{Condvar, Mutex}; + + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + // Wait until our parent has taken the lock. + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + + // Set the data to `true` and wake up our parent. + let mut guard = maybe_unwrap(lock.lock()); + *guard = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut guard = maybe_unwrap(lock.lock()); + // Wake up our child. + tx.send(()).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + guard = maybe_unwrap(cvar.wait(guard)); } + } +); + +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +nonpoison_and_poison_unwrap_test!( + name: wait_while, + test_body: { + use locks::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); - break; + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = maybe_unwrap(lock.lock()); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started); + assert!(*maybe_unwrap(guard)); } -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_while_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); - // no spurious wakeups. ensure it timed-out - assert!(wait.timed_out()); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_while_instant_satisfy() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); -} - -#[test] +); + #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_timeout_while_wake() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair_copy = pair.clone(); - - let &(ref m, ref c) = &*pair; - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair_copy; - let mut started = lock.lock().unwrap(); - thread::sleep(Duration::from_millis(1)); - *started = true; - cvar.notify_one(); - }); - let (g2, wait) = c - .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) - .unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - assert!(*g2); -} - -#[test] +nonpoison_and_poison_unwrap_test!( + name: wait_timeout_wait, + test_body: { + use locks::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = maybe_unwrap(m.lock()); + let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1))); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } + + break; + } + } +); + #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_timeout_wake() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); +nonpoison_and_poison_unwrap_test!( + name: wait_timeout_while_wait, + test_body: { + use locks::{Condvar, Mutex}; - loop { - let g = m.lock().unwrap(); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - let m2 = m.clone(); + let g = maybe_unwrap(m.lock()); + let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true)); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); + } +); + +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +nonpoison_and_poison_unwrap_test!( + name: wait_timeout_while_instant_satisfy, + test_body: { + use locks::{Condvar, Mutex}; + + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = maybe_unwrap(m.lock()); + let (_g, wait) = + maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false)); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + } +); - let notified = Arc::new(AtomicBool::new(false)); - let notified_copy = notified.clone(); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +nonpoison_and_poison_unwrap_test!( + name: wait_timeout_while_wake, + test_body: { + use locks::{Condvar, Mutex}; + + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); - let t = thread::spawn(move || { - let _g = m2.lock().unwrap(); + let &(ref m, ref c) = &*pair; + let g = maybe_unwrap(m.lock()); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = maybe_unwrap(lock.lock()); thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::Relaxed); - c2.notify_one(); + *started = true; + cvar.notify_one(); }); - let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); - assert!(!timeout_res.timed_out()); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not notified - if !notified.load(Ordering::Relaxed) { - t.join().unwrap(); - continue; - } - drop(g); + let (g2, wait) = maybe_unwrap(c.wait_timeout_while( + g, + Duration::from_millis(u64::MAX), + |&mut notified| !notified + )); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); + } +); + +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +nonpoison_and_poison_unwrap_test!( + name: wait_timeout_wake, + test_body: { + use locks::{Condvar, Mutex}; - t.join().unwrap(); + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); - break; + loop { + let g = maybe_unwrap(m.lock()); + + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); + + let t = thread::spawn(move || { + let _g = maybe_unwrap(m2.lock()); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); + }); + let (g, timeout_res) = + maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX))); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { + t.join().unwrap(); + continue; + } + drop(g); + + t.join().unwrap(); + + break; + } } -} +); diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index f874c2ba38951..ac1dbebcc5cb0 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -7,6 +7,7 @@ #![feature(rwlock_downgrade)] #![feature(std_internals)] #![feature(sync_nonpoison)] +#![feature(nonpoison_condvar)] #![feature(nonpoison_mutex)] #![feature(nonpoison_rwlock)] #![allow(internal_features)] diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 90cefc0d59466..612c75c7aef51 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -213,40 +213,6 @@ nonpoison_and_poison_unwrap_test!( } ); -// FIXME(nonpoison_condvar): Move this to the `condvar.rs` test file once `nonpoison::condvar` gets -// implemented. -#[test] -fn test_mutex_arc_condvar() { - struct Packet(Arc<(Mutex, Condvar)>); - - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - - let (tx, rx) = channel(); - - let _t = thread::spawn(move || { - // Wait until our parent has taken the lock. - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - - // Set the data to `true` and wake up our parent. - let mut guard = lock.lock().unwrap(); - *guard = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut guard = lock.lock().unwrap(); - // Wake up our child. - tx.send(()).unwrap(); - - // Wait until our child has set the data to `true`. - assert!(!*guard); - while !*guard { - guard = cvar.wait(guard).unwrap(); - } -} - nonpoison_and_poison_unwrap_test!( name: test_mutex_arc_nested, test_body: { From a8163bd6f4f2bc3ba8fb56df9d48622070ba68ab Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Tue, 22 Jul 2025 13:52:10 +0200 Subject: [PATCH 5/9] change `Barrier` implementation to use `nonpoison::Condvar` Signed-off-by: Connor Tsui --- library/std/src/sync/barrier.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 067ff66d9af73..712ce03f90b02 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,6 +1,5 @@ use crate::fmt; -// FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available -use crate::sync::{Condvar, Mutex}; +use crate::sync::nonpoison::{Condvar, Mutex}; /// A barrier enables multiple threads to synchronize the beginning /// of some computation. @@ -118,12 +117,11 @@ impl Barrier { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn wait(&self) -> BarrierWaitResult { - let mut lock = self.lock.lock().unwrap(); + let mut lock = self.lock.lock(); let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - let _guard = - self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap(); + let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id); BarrierWaitResult(false) } else { lock.count = 0; From b8ee38b79e6bfbb1376785f765a233e96d0c5da2 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Thu, 24 Jul 2025 15:24:22 +0200 Subject: [PATCH 6/9] move `WaitTimeoutResult` up to `mod.rs` Since `WaitTimeoutResult` is poison-agnostic, we want to use the same type for both variants of `Condvar`. Signed-off-by: Connor Tsui --- library/std/src/sync/mod.rs | 65 ++++++++++++++++++++- library/std/src/sync/nonpoison.rs | 2 +- library/std/src/sync/nonpoison/condvar.rs | 70 +---------------------- library/std/src/sync/poison.rs | 2 +- library/std/src/sync/poison/condvar.rs | 66 +-------------------- 5 files changed, 68 insertions(+), 137 deletions(-) diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 6ef3bf25cf67c..97c04d07eaf1d 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -209,7 +209,7 @@ pub use self::poison::{LockResult, PoisonError}; #[doc(inline)] pub use self::poison::{ Mutex, MutexGuard, TryLockError, TryLockResult, - Condvar, WaitTimeoutResult, + Condvar, Once, OnceState, RwLock, RwLockReadGuard, RwLockWriteGuard, }; @@ -234,3 +234,66 @@ mod barrier; mod lazy_lock; mod once_lock; mod reentrant_lock; + +/// A type indicating whether a timed wait on a condition variable returned +/// due to a time out or not. +/// +/// It is returned by the [`wait_timeout`] method. +/// +/// [`wait_timeout`]: Condvar::wait_timeout +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[stable(feature = "wait_timeout", since = "1.5.0")] +pub struct WaitTimeoutResult(bool); + +impl WaitTimeoutResult { + /// Returns `true` if the wait was known to have timed out. + /// + /// # Examples + /// + /// 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 10 millisecond timeout on the condvar + /// and will leave the loop upon timeout. + /// + /// ``` + /// use std::sync::{Arc, Condvar, Mutex}; + /// use std::thread; + /// use std::time::Duration; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// # let handle = + /// thread::spawn(move || { + /// let (lock, cvar) = &*pair2; + /// + /// // Let's wait 20 milliseconds before notifying the condvar. + /// thread::sleep(Duration::from_millis(20)); + /// + /// let mut started = lock.lock().unwrap(); + /// // We update the boolean value. + /// *started = true; + /// cvar.notify_one(); + /// }); + /// + /// // Wait for the thread to start up. + /// let (lock, cvar) = &*pair; + /// loop { + /// // Let's put a timeout on the condvar's wait. + /// 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 + /// } + /// } + /// # // Prevent leaks for Miri. + /// # let _ = handle.join(); + /// ``` + #[must_use] + #[stable(feature = "wait_timeout", since = "1.5.0")] + pub fn timed_out(&self) -> bool { + self.0 + } +} diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs index 96ecf7084fa94..ec3587263f470 100644 --- a/library/std/src/sync/nonpoison.rs +++ b/library/std/src/sync/nonpoison.rs @@ -30,7 +30,7 @@ impl fmt::Display for WouldBlock { } #[unstable(feature = "nonpoison_condvar", issue = "134645")] -pub use self::condvar::{Condvar, WaitTimeoutResult}; +pub use self::condvar::Condvar; #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub use self::mutex::MappedMutexGuard; #[unstable(feature = "nonpoison_mutex", issue = "134645")] diff --git a/library/std/src/sync/nonpoison/condvar.rs b/library/std/src/sync/nonpoison/condvar.rs index 9744c87d9cc1e..49afdd878182f 100644 --- a/library/std/src/sync/nonpoison/condvar.rs +++ b/library/std/src/sync/nonpoison/condvar.rs @@ -1,77 +1,9 @@ use crate::fmt; +use crate::sync::WaitTimeoutResult; use crate::sync::nonpoison::{MutexGuard, mutex}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; -/// A type indicating whether a timed wait on a condition variable returned -/// due to a time out or not. -/// -/// It is returned by the [`wait_timeout`] method. -/// -/// [`wait_timeout`]: Condvar::wait_timeout -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[unstable(feature = "nonpoison_condvar", issue = "134645")] -pub struct WaitTimeoutResult(bool); - -// FIXME(nonpoison_condvar) this type is duplicated in `poison`. How do we share types that are -// poison-agnostic? -impl WaitTimeoutResult { - /// Returns `true` if the wait was known to have timed out. - /// - /// # Examples - /// - /// 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 10 millisecond timeout on the condvar - /// and will leave the loop upon timeout. - /// - /// ``` - /// #![feature(nonpoison_mutex)] - /// #![feature(nonpoison_condvar)] - /// - /// use std::sync::nonpoison::{Mutex, Condvar}; - /// use std::sync::Arc; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// # let handle = - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// - /// // Let's wait 20 milliseconds before notifying the condvar. - /// thread::sleep(Duration::from_millis(20)); - /// - /// let mut started = lock.lock(); - /// // We update the boolean value. - /// *started = true; - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// loop { - /// // Let's put a timeout on the condvar's wait. - /// let result = cvar.wait_timeout(lock.lock(), Duration::from_millis(10)); - /// // 10 milliseconds have passed. - /// if result.1.timed_out() { - /// // timed out now and we can leave. - /// break - /// } - /// } - /// # // Prevent leaks for Miri. - /// # let _ = handle.join(); - /// ``` - #[must_use] - #[unstable(feature = "nonpoison_condvar", issue = "134645")] - pub fn timed_out(&self) -> bool { - self.0 - } -} - /// A Condition Variable /// /// For more information about condition variables, check out the documentation for the poisoning diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 31889dcc10fad..dc4774dcca181 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -61,7 +61,7 @@ //! then the lock will not be poisoned. #[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::{Condvar, WaitTimeoutResult}; +pub use self::condvar::Condvar; #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub use self::mutex::MappedMutexGuard; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index 0e9d4233c6577..5dc2b510f3a2b 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -1,73 +1,9 @@ use crate::fmt; +use crate::sync::WaitTimeoutResult; use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; use crate::time::{Duration, Instant}; -/// A type indicating whether a timed wait on a condition variable returned -/// due to a time out or not. -/// -/// It is returned by the [`wait_timeout`] method. -/// -/// [`wait_timeout`]: Condvar::wait_timeout -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -#[stable(feature = "wait_timeout", since = "1.5.0")] -pub struct WaitTimeoutResult(bool); - -// FIXME(nonpoison_condvar): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. -// Should we take advantage of this fact? -impl WaitTimeoutResult { - /// Returns `true` if the wait was known to have timed out. - /// - /// # Examples - /// - /// 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 10 millisecond timeout on the condvar - /// and will leave the loop upon timeout. - /// - /// ``` - /// use std::sync::{Arc, Condvar, Mutex}; - /// use std::thread; - /// use std::time::Duration; - /// - /// let pair = Arc::new((Mutex::new(false), Condvar::new())); - /// let pair2 = Arc::clone(&pair); - /// - /// # let handle = - /// thread::spawn(move || { - /// let (lock, cvar) = &*pair2; - /// - /// // Let's wait 20 milliseconds before notifying the condvar. - /// thread::sleep(Duration::from_millis(20)); - /// - /// let mut started = lock.lock().unwrap(); - /// // We update the boolean value. - /// *started = true; - /// cvar.notify_one(); - /// }); - /// - /// // Wait for the thread to start up. - /// let (lock, cvar) = &*pair; - /// loop { - /// // Let's put a timeout on the condvar's wait. - /// 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 - /// } - /// } - /// # // Prevent leaks for Miri. - /// # let _ = handle.join(); - /// ``` - #[must_use] - #[stable(feature = "wait_timeout", since = "1.5.0")] - pub fn timed_out(&self) -> bool { - self.0 - } -} - /// A Condition Variable /// /// Condition variables represent the ability to block a thread such that it From d210ce7dac4acb7653d647c77d56c10068dda82c Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Sat, 23 Aug 2025 09:39:37 -0400 Subject: [PATCH 7/9] fix visibility of private getters Signed-off-by: Connor Tsui --- library/std/src/sync/nonpoison/mutex.rs | 3 ++- library/std/src/sync/poison/mutex.rs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index f8af081e79d95..07430ce3a1393 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -457,7 +457,8 @@ impl fmt::Display for MutexGuard<'_, T> { } } -pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { +/// For use in [`nonpoison::condvar`](super::condvar). +pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { &guard.lock.inner } diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 720c212c65cf7..7e9d920d92f85 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -757,11 +757,13 @@ impl fmt::Display for MutexGuard<'_, T> { } } -pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { +/// For use in [`nonpoison::condvar`](super::condvar). +pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { &guard.lock.inner } -pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { +/// For use in [`nonpoison::condvar`](super::condvar). +pub(super) fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { &guard.lock.poison } From 8134a10ec76e7c357d207ea83b215a15af63519c Mon Sep 17 00:00:00 2001 From: actuallylost Date: Fri, 29 Aug 2025 23:58:32 +0300 Subject: [PATCH 8/9] Add `Duration::from_nanos_u128` Tracking issue: RUST-139201 Co-authored-by: omanirudh --- library/core/src/time.rs | 36 +++++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + library/coretests/tests/time.rs | 19 +++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index f37e47f132d54..d205bc376f125 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -308,6 +308,42 @@ impl Duration { Duration { secs, nanos: subsec_nanos } } + /// Creates a new `Duration` from the specified number of nanoseconds. + /// + /// # Panics + /// + /// Panics if the given number of nanoseconds is greater than [`Duration::MAX`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_from_nanos_u128)] + /// use std::time::Duration; + /// + /// let nanos = 10_u128.pow(24) + 321; + /// let duration = Duration::from_nanos_u128(nanos); + /// + /// assert_eq!(10_u64.pow(15), duration.as_secs()); + /// assert_eq!(321, duration.subsec_nanos()); + /// ``` + #[unstable(feature = "duration_from_nanos_u128", issue = "139201")] + // This is necessary because of const `try_from`, but can be removed if a trait-free impl is used instead + #[rustc_const_unstable(feature = "duration_from_nanos_u128", issue = "139201")] + #[must_use] + #[inline] + #[track_caller] + pub const fn from_nanos_u128(nanos: u128) -> Duration { + const NANOS_PER_SEC: u128 = self::NANOS_PER_SEC as u128; + let Ok(secs) = u64::try_from(nanos / NANOS_PER_SEC) else { + panic!("overflow in `Duration::from_nanos_u128`"); + }; + let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; + // SAFETY: x % 1_000_000_000 < 1_000_000_000 also, subsec_nanos >= 0 since u128 >=0 and u32 >=0 + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; + + Duration { secs: secs as u64, nanos: subsec_nanos } + } + /// Creates a new `Duration` from the specified number of weeks. /// /// # Panics diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b9b768f29d788..c95643f0c8e33 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -36,6 +36,7 @@ #![feature(drop_guard)] #![feature(duration_constants)] #![feature(duration_constructors)] +#![feature(duration_from_nanos_u128)] #![feature(error_generic_member_access)] #![feature(exact_div)] #![feature(exact_size_is_empty)] diff --git a/library/coretests/tests/time.rs b/library/coretests/tests/time.rs index bb98e59bf5a2b..fb3c50f9bde98 100644 --- a/library/coretests/tests/time.rs +++ b/library/coretests/tests/time.rs @@ -45,6 +45,14 @@ fn from_weeks_overflow() { let _ = Duration::from_weeks(overflow); } +#[test] +#[should_panic] +fn from_nanos_u128_overflow() { + let nanos_per_sec: u128 = 1_000_000_000; + let overflow = (u64::MAX as u128 * nanos_per_sec) + (nanos_per_sec - 1) + 1; + let _ = Duration::from_nanos_u128(overflow); +} + #[test] fn constructor_weeks() { assert_eq!(Duration::from_weeks(1), Duration::from_secs(7 * 24 * 60 * 60)); @@ -81,6 +89,8 @@ fn secs() { assert_eq!(Duration::from_micros(1_000_001).as_secs(), 1); assert_eq!(Duration::from_nanos(999_999_999).as_secs(), 0); assert_eq!(Duration::from_nanos(1_000_000_001).as_secs(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).as_secs(), 0); + assert_eq!(Duration::from_nanos_u128(1_000_000_001).as_secs(), 1); } #[test] @@ -95,6 +105,8 @@ fn millis() { assert_eq!(Duration::from_micros(1_001_000).subsec_millis(), 1); assert_eq!(Duration::from_nanos(999_999_999).subsec_millis(), 999); assert_eq!(Duration::from_nanos(1_001_000_000).subsec_millis(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_millis(), 999); + assert_eq!(Duration::from_nanos_u128(1_001_000_001).subsec_millis(), 1); } #[test] @@ -109,6 +121,8 @@ fn micros() { assert_eq!(Duration::from_micros(1_000_001).subsec_micros(), 1); assert_eq!(Duration::from_nanos(999_999_999).subsec_micros(), 999_999); assert_eq!(Duration::from_nanos(1_000_001_000).subsec_micros(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_micros(), 999_999); + assert_eq!(Duration::from_nanos_u128(1_000_001_000).subsec_micros(), 1); } #[test] @@ -123,6 +137,8 @@ fn nanos() { assert_eq!(Duration::from_micros(1_000_001).subsec_nanos(), 1000); assert_eq!(Duration::from_nanos(999_999_999).subsec_nanos(), 999_999_999); assert_eq!(Duration::from_nanos(1_000_000_001).subsec_nanos(), 1); + assert_eq!(Duration::from_nanos_u128(999_999_999).subsec_nanos(), 999_999_999); + assert_eq!(Duration::from_nanos_u128(1_000_000_001).subsec_nanos(), 1); } #[test] @@ -520,6 +536,9 @@ fn duration_const() { const FROM_NANOS: Duration = Duration::from_nanos(1_000_000_000); assert_eq!(FROM_NANOS, Duration::SECOND); + const FROM_NANOS_U128: Duration = Duration::from_nanos_u128(NANOS); + assert_eq!(FROM_NANOS_U128, Duration::SECOND); + const MAX: Duration = Duration::new(u64::MAX, 999_999_999); const CHECKED_ADD: Option = MAX.checked_add(Duration::SECOND); From 2c21b884a59814836cbd9386492d9ba71039fc75 Mon Sep 17 00:00:00 2001 From: "Chai T. Rex" Date: Sat, 23 Aug 2025 17:53:27 -0400 Subject: [PATCH 9/9] Optimize `.ilog({2,10})` to `.ilog{2,10}()` Inform compiler of optimizations when the base is known at compile time and there's a cheaper method available: * `{integer}.checked_ilog(2)` -> `{integer}.checked_ilog2()` * `{integer}.checked_ilog(10)` -> `{integer}.checked_ilog10()` * `{integer}.ilog(2)` -> `{integer}.ilog2()` * `{integer}.ilog(10)` -> `{integer}.ilog10()` --- library/core/src/num/uint_macros.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 10d9498d15e45..acf4ca9a287c2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1491,6 +1491,20 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_ilog(self, base: Self) -> Option { + // Inform compiler of optimizations when the base is known at + // compile time and there's a cheaper method available. + // + // Note: Like all optimizations, this is not guaranteed to be + // applied by the compiler. If you want those specific bases, + // use `.checked_ilog2()` or `.checked_ilog10()` directly. + if core::intrinsics::is_val_statically_known(base) { + if base == 2 { + return self.checked_ilog2(); + } else if base == 10 { + return self.checked_ilog10(); + } + } + if self <= 0 || base <= 1 { None } else if self < base {