Skip to content

Commit

Permalink
move GILOnceCell initialization detail to the type-level docs
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 26, 2022
1 parent 01fcfed commit a47079d
Showing 1 changed file with 15 additions and 14 deletions.
29 changes: 15 additions & 14 deletions src/once_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ use std::cell::UnsafeCell;
/// A write-once cell similar to [`once_cell::OnceCell`](https://docs.rs/once_cell/latest/once_cell/).
///
/// Unlike `once_cell::sync` which blocks threads to achieve thread safety, this implementation
/// uses the Python GIL to mediate concurrent access. This helps in cases where `once_sync` or
/// uses the Python GIL to mediate concurrent access. This helps in cases where `once_cell` or
/// `lazy_static`'s synchronization strategy can lead to deadlocks when interacting with the Python
/// GIL. For an example, see [the FAQ section](https://pyo3.rs/latest/faq.html) of the guide.
///
/// Note that:
/// 1) `get_or_init` and `get_or_try_init` do not protect against infinite recursion
/// from reentrant initialization.
/// 2) If the initialization function `f` provided to `get_or_init` (or `get_or_try_init`)
/// temporarily releases the GIL (e.g. by calling `Python::import`) then it is possible
/// for a second thread to also begin initializing the `GITOnceCell`. Even when this
/// happens `GILOnceCell` guarantees that only **one** write to the cell ever occurs
/// - this is treated as a race, other threads will discard the value they compute and
/// return the result of the first complete computation.
///
/// # Examples
///
/// The following example shows how to use `GILOnceCell` to share a reference to a Python list
Expand Down Expand Up @@ -51,17 +61,7 @@ impl<T> GILOnceCell<T> {
/// Get a reference to the contained value, initializing it if needed using the provided
/// closure.
///
/// Note that:
/// 1) reentrant initialization can cause a stack overflow.
/// 2) if f() temporarily releases the GIL (e.g. by calling `Python::import`) then it is
/// possible (and well-defined) that a second thread may also call get_or_init and begin
/// calling `f()`. Even when this happens `GILOnceCell` guarantees that only **one** write
/// to the cell ever occurs - other threads will simply discard the value they compute and
/// return the result of the first complete computation.
/// 3) if f() does not release the GIL and does not panic, it is guaranteed to be called
/// exactly once, even if multiple threads attempt to call `get_or_init`
/// 4) if f() can panic but still does not release the GIL, it may be called multiple times,
/// but it is guaranteed that f() will never be called concurrently
/// See the type-level documentation for detail on re-entrancy and concurrent initialization.
#[inline]
pub fn get_or_init<F>(&self, py: Python<'_>, f: F) -> &T
where
Expand All @@ -77,9 +77,10 @@ impl<T> GILOnceCell<T> {
}
}

/// Like `get_or_init`, but accepts a fallible initialization function.
/// Like `get_or_init`, but accepts a fallible initialization function. If it fails, the cell
/// is left uninitialized.
///
/// If it fails, the cell is left uninitialized.
/// See the type-level documentation for detail on re-entrancy and concurrent initialization.
#[inline]
pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
where
Expand Down

0 comments on commit a47079d

Please sign in to comment.