1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use std::cell::UnsafeCell;
use pyo3::{PyResult, Python};
// Adapted from PYO3 with the only change that
// we allow mutable access with when the GIL is held
pub struct GILOnceCell<T>(UnsafeCell<Option<T>>);
// T: Send is needed for Sync because the thread which drops the GILOnceCell can be different
// to the thread which fills it.
unsafe impl<T: Send + Sync> Sync for GILOnceCell<T> {}
unsafe impl<T: Send> Send for GILOnceCell<T> {}
impl<T> GILOnceCell<T> {
/// Create a `GILOnceCell` which does not yet contain a value.
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self(UnsafeCell::new(None))
}
/// as long as we have the GIL we can mutate
/// this creates a context that checks that.
pub fn with_gil<F, O>(&self, _py: Python<'_>, mut op: F) -> PyResult<O>
where
F: FnMut(&mut T) -> PyResult<O>,
{
// Safe because GIL is held, so no other thread can be writing to this cell concurrently.
let inner = unsafe { &mut *self.0.get() }
.as_mut()
.expect("not yet initialized");
op(inner)
}
/// 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.
pub fn set(&self, _py: Python<'_>, value: T) -> Result<(), T> {
// Safe because GIL is held, so no other thread can be writing to this cell concurrently.
let inner = unsafe { &mut *self.0.get() };
if inner.is_some() {
return Err(value);
}
*inner = Some(value);
Ok(())
}
}