Skip to content

Document Poisoning in LazyCell and LazyLock #144872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions library/core/src/cell/lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@ enum State<T, F> {
///
/// [`std::sync::LazyLock`]: ../../std/sync/struct.LazyLock.html
///
/// # Poisoning
///
/// If the initialization closure passed to [`LazyCell::new`] panics, the cell will be poisoned.
/// Once the cell is poisoned, any threads that attempt to access this cell (via a dereference
/// or via an explicit call to [`force()`]) will panic.
///
/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key
/// difference, however, is that poisoning in `LazyCell` is _unrecoverable_. All future accesses of
/// the cell from other threads will panic, whereas a type in [`std::sync::poison`] like
/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`].
///
/// [`force()`]: LazyCell::force
/// [`std::sync::poison`]: ../../std/sync/poison/index.html
/// [`std::sync::poison::Mutex`]: ../../std/sync/poison/struct.Mutex.html
/// [`PoisonError::into_inner()`]: ../../std/sync/poison/struct.PoisonError.html#method.into_inner
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -64,6 +80,10 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
///
/// # Panics
///
/// Panics if the cell is poisoned.
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -93,6 +113,15 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
///
/// This is equivalent to the `Deref` impl, but is explicit.
///
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the cell becomes poisoned. This will cause all future
/// accesses of the cell (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyCell::new
/// [`force()`]: LazyCell::force
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -123,6 +152,15 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
/// Forces the evaluation of this lazy value and returns a mutable reference to
/// the result.
///
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the cell becomes poisoned. This will cause all future
/// accesses of the cell (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyCell::new
/// [`force()`]: LazyCell::force
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -219,7 +257,8 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> {
}

impl<T, F> LazyCell<T, F> {
/// Returns a mutable reference to the value if initialized, or `None` if not.
/// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or
/// poisoned), returns `None`.
///
/// # Examples
///
Expand All @@ -245,7 +284,8 @@ impl<T, F> LazyCell<T, F> {
}
}

/// Returns a reference to the value if initialized, or `None` if not.
/// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned),
/// returns `None`.
///
/// # Examples
///
Expand Down Expand Up @@ -278,6 +318,15 @@ impl<T, F> LazyCell<T, F> {
#[stable(feature = "lazy_cell", since = "1.80.0")]
impl<T, F: FnOnce() -> T> Deref for LazyCell<T, F> {
type Target = T;

/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the cell becomes poisoned. This will cause all future
/// accesses of the cell (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyCell::new
/// [`force()`]: LazyCell::force
#[inline]
fn deref(&self) -> &T {
LazyCell::force(self)
Expand All @@ -286,6 +335,14 @@ impl<T, F: FnOnce() -> T> Deref for LazyCell<T, F> {

#[stable(feature = "lazy_deref_mut", since = "1.89.0")]
impl<T, F: FnOnce() -> T> DerefMut for LazyCell<T, F> {
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the cell becomes poisoned. This will cause all future
/// accesses of the cell (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyCell::new
/// [`force()`]: LazyCell::force
#[inline]
fn deref_mut(&mut self) -> &mut T {
LazyCell::force_mut(self)
Expand Down
60 changes: 58 additions & 2 deletions library/std/src/sync/lazy_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ union Data<T, F> {
///
/// [`LazyCell`]: crate::cell::LazyCell
///
/// # Poisoning
///
/// If the initialization closure passed to [`LazyLock::new`] panics, the lock will be poisoned.
/// Once the lock is poisoned, any threads that attempt to access this lock (via a dereference
/// or via an explicit call to [`force()`]) will panic.
///
/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key
/// difference, however, is that poisoning in `LazyLock` is _unrecoverable_. All future accesses of
/// the lock from other threads will panic, whereas a type in [`std::sync::poison`] like
/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`].
///
/// [`force()`]: LazyLock::force
/// [`std::sync::poison`]: crate::sync::poison
/// [`std::sync::poison::Mutex`]: crate::sync::poison::Mutex
/// [`PoisonError::into_inner()`]: crate::sync::poison::PoisonError::into_inner
///
/// # Examples
///
/// Initialize static variables with `LazyLock`.
Expand Down Expand Up @@ -102,6 +118,10 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
///
/// # Panics
///
/// Panics if the lock is poisoned.
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -136,6 +156,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// Forces the evaluation of this lazy value and returns a mutable reference to
/// the result.
///
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
/// accesses of the lock (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyLock::new
/// [`force()`]: LazyLock::force
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -193,6 +222,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
/// This method will block the calling thread if another initialization
/// routine is currently running.
///
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
/// accesses of the lock (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyLock::new
/// [`force()`]: LazyLock::force
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -227,7 +265,8 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
}

impl<T, F> LazyLock<T, F> {
/// Returns a mutable reference to the value if initialized, or `None` if not.
/// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or
/// poisoned), returns `None`.
///
/// # Examples
///
Expand Down Expand Up @@ -256,7 +295,8 @@ impl<T, F> LazyLock<T, F> {
}
}

/// Returns a reference to the value if initialized, or `None` if not.
/// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned),
/// returns `None`.
///
/// # Examples
///
Expand Down Expand Up @@ -307,6 +347,14 @@ impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
/// This method will block the calling thread if another initialization
/// routine is currently running.
///
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
/// accesses of the lock (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyLock::new
/// [`force()`]: LazyLock::force
#[inline]
fn deref(&self) -> &T {
LazyLock::force(self)
Expand All @@ -315,6 +363,14 @@ impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {

#[stable(feature = "lazy_deref_mut", since = "1.89.0")]
impl<T, F: FnOnce() -> T> DerefMut for LazyLock<T, F> {
/// # Panics
///
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
/// accesses of the lock (via [`force()`] or a dereference) to panic.
///
/// [`new()`]: LazyLock::new
/// [`force()`]: LazyLock::force
#[inline]
fn deref_mut(&mut self) -> &mut T {
LazyLock::force_mut(self)
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sync/poison.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//!
//! The specifics of how this "poisoned" state affects other threads and whether
//! the panics are recognized reliably or on a best-effort basis depend on the
//! primitive. See [#Overview] below.
//! primitive. See [Overview](#overview) below.
//!
//! For the alternative implementations that do not employ poisoning,
//! see [`std::sync::nonpoison`].
Expand Down
Loading