-
Notifications
You must be signed in to change notification settings - Fork 678
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3e905eb
commit 0b8b19c
Showing
5 changed files
with
123 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use crate::Python; | ||
use std::cell::UnsafeCell; | ||
|
||
/// A write-once cell similar to once_cell::OnceCell. | ||
/// | ||
/// Unlike once_cell::sync which uses locks to achieve thread safety, this implementation uses | ||
/// the Python GIL to mediate concurrent access. | ||
pub struct OnceCell<T>(UnsafeCell<Option<T>>); | ||
|
||
// T: Send is needed for Sync because the thread which drops the OnceCell can be different | ||
// to the thread which fills it. | ||
unsafe impl<T: Send + Sync> Sync for OnceCell<T> {} | ||
unsafe impl<T: Send> Send for OnceCell<T> {} | ||
|
||
impl<T> OnceCell<T> { | ||
pub const fn new() -> Self { | ||
Self(UnsafeCell::new(None)) | ||
} | ||
|
||
pub fn get(&self, _py: Python) -> Option<&T> { | ||
// Safe because if the cell has not yet been written, None is returned. | ||
unsafe { &*self.0.get() }.as_ref() | ||
} | ||
|
||
pub fn get_or_init<F>(&self, py: Python, f: F) -> &T | ||
where | ||
F: FnOnce() -> T, | ||
{ | ||
let inner = unsafe { &*self.0.get() }.as_ref(); | ||
if let Some(value) = inner { | ||
return value; | ||
} | ||
|
||
// Note that f() could release the GIL, so it's possible that another thread writes to this | ||
// OnceCell before f() finishes. That's fine; we'll just have to discard the value | ||
// computed here and accept a bit of wasted computation. | ||
let value = f(); | ||
let _ = self.set(py, value); | ||
|
||
self.get(py).unwrap() | ||
} | ||
|
||
pub fn get_mut(&mut self) -> Option<&mut T> { | ||
// Safe because we have &mut self | ||
unsafe { &mut *self.0.get() }.as_mut() | ||
} | ||
|
||
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(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters