pub struct GILOnceCell<T> {
once: Once,
data: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>,
}
Expand description
A write-once primitive similar to std::sync::OnceLock<T>
.
Unlike OnceLock<T>
which blocks threads to achieve thread safety, GilOnceCell<T>
allows calls to get_or_init
and
get_or_try_init
to race to create an initialized value.
(It is still guaranteed that only one thread will ever write to the cell.)
On Python versions that run with the Global Interpreter Lock (GIL), this helps to avoid deadlocks between initialization and the GIL. For an example of such a deadlock, see the FAQ section of the guide.
Note that because the GIL blocks concurrent execution, in practice the means that
get_or_init
and
get_or_try_init
may race if the initialization
function leads to the GIL being released and a thread context switch. This can
happen when importing or calling any Python code, as long as it releases the
GIL at some point. On free-threaded Python without any GIL, the race is
more likely since there is no GIL to prevent races. In the future, PyO3 may change
the semantics of GILOnceCell to behave more like the GIL build in the future.
§Re-entrant initialization
get_or_init
and
get_or_try_init
do not protect against infinite recursion
from reentrant initialization.
§Examples
The following example shows how to use GILOnceCell
to share a reference to a Python list
between threads:
use pyo3::sync::GILOnceCell;
use pyo3::prelude::*;
use pyo3::types::PyList;
static LIST_CELL: GILOnceCell<Py<PyList>> = GILOnceCell::new();
pub fn get_shared_list(py: Python<'_>) -> &Bound<'_, PyList> {
LIST_CELL
.get_or_init(py, || PyList::empty(py).unbind())
.bind(py)
}
Fields§
§once: Once
§data: UnsafeCell<MaybeUninit<T>>
§_marker: PhantomData<T>
(Copied from std::sync::OnceLock)
PhantomData
to make sure dropck understands we’re dropping T in our Drop impl.
use pyo3::Python;
use pyo3::sync::GILOnceCell;
struct A<'a>(#[allow(dead_code)] &'a str);
impl<'a> Drop for A<'a> {
fn drop(&mut self) {}
}
let cell = GILOnceCell::new();
{
let s = String::new();
let _ = Python::with_gil(|py| cell.set(py,A(&s)));
}
Implementations§
Source§impl<T> GILOnceCell<T>
impl<T> GILOnceCell<T>
Sourcepub fn get(&self, _py: Python<'_>) -> Option<&T>
pub fn get(&self, _py: Python<'_>) -> Option<&T>
Get a reference to the contained value, or None
if the cell has not yet been written.
Sourcepub fn get_or_init<F>(&self, py: Python<'_>, f: F) -> &Twhere
F: FnOnce() -> T,
pub fn get_or_init<F>(&self, py: Python<'_>, f: F) -> &Twhere
F: FnOnce() -> T,
Get a reference to the contained value, initializing it if needed using the provided closure.
See the type-level documentation for detail on re-entrancy and concurrent initialization.
Sourcepub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
pub fn get_or_try_init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
Like get_or_init
, but accepts a fallible initialization function. If it fails, the cell
is left uninitialized.
See the type-level documentation for detail on re-entrancy and concurrent initialization.
fn init<F, E>(&self, py: Python<'_>, f: F) -> Result<&T, E>
Sourcepub fn get_mut(&mut self) -> Option<&mut T>
pub fn get_mut(&mut self) -> Option<&mut T>
Get the contents of the cell mutably. This is only possible if the reference to the cell is unique.
Sourcepub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T>
pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T>
Set the value in the cell.
If the cell has already been written, Err(value)
will be returned containing the new
value which was not written.
Sourcepub fn take(&mut self) -> Option<T>
pub fn take(&mut self) -> Option<T>
Takes the value out of the cell, moving it back to an uninitialized state.
Has no effect and returns None if the cell has not yet been written.
Sourcepub fn into_inner(self) -> Option<T>
pub fn into_inner(self) -> Option<T>
Consumes the cell, returning the wrapped value.
Returns None if the cell has not yet been written.
Source§impl<T> GILOnceCell<Py<T>>
impl<T> GILOnceCell<Py<T>>
Source§impl<T> GILOnceCell<Py<T>>where
T: PyTypeCheck,
impl<T> GILOnceCell<Py<T>>where
T: PyTypeCheck,
Sourcepub fn import<'py>(
&self,
py: Python<'py>,
module_name: &str,
attr_name: &str,
) -> PyResult<&Bound<'py, T>>
pub fn import<'py>( &self, py: Python<'py>, module_name: &str, attr_name: &str, ) -> PyResult<&Bound<'py, T>>
Get a reference to the contained Python type, initializing the cell if needed.
This is a shorthand method for get_or_init
which imports the type from Python on init.
§Example: Using GILOnceCell
to store a class in a static variable.
GILOnceCell
can be used to avoid importing a class multiple times:
#[pyfunction]
fn create_ordered_dict<'py>(py: Python<'py>, dict: Bound<'py, PyDict>) -> PyResult<Bound<'py, PyAny>> {
// Even if this function is called multiple times,
// the `OrderedDict` class will be imported only once.
static ORDERED_DICT: GILOnceCell<Py<PyType>> = GILOnceCell::new();
ORDERED_DICT
.import(py, "collections", "OrderedDict")?
.call1((dict,))
}
Trait Implementations§
Source§impl<T> Default for GILOnceCell<T>
impl<T> Default for GILOnceCell<T>
Source§impl<T> Drop for GILOnceCell<T>
impl<T> Drop for GILOnceCell<T>
impl<T: Send> Send for GILOnceCell<T>
impl<T: Send + Sync> Sync for GILOnceCell<T>
Auto Trait Implementations§
impl<T> !Freeze for GILOnceCell<T>
impl<T> !RefUnwindSafe for GILOnceCell<T>
impl<T> Unpin for GILOnceCell<T>where
T: Unpin,
impl<T> UnwindSafe for GILOnceCell<T>where
T: UnwindSafe,
Blanket Implementations§
Source§impl<T> AssertNotZeroSized for T
impl<T> AssertNotZeroSized for T
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more