-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Add OnceCell #3591
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
Add OnceCell #3591
Changes from 1 commit
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
b3e521d
add OnceCell
b-naber 440a547
add and update tests
b-naber fdadf94
address review
b-naber 02dcffc
address review
b-naber c539d68
fix clippy warnings
b-naber 9940f2c
fix docs errors
b-naber 3ca002f
address review
b-naber b76ee23
address review
b-naber 1f8753a
update
b-naber 7b1be1a
add destructor
b-naber 741b508
suggestions
b-naber cdc3edc
suggestions
b-naber a92ce09
add get_or_try_init
b-naber b58dda5
suggestions
b-naber 201cbf1
fix docs
b-naber File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
address review
- Loading branch information
commit fdadf94457761ef1befcfac4084fe68390c68c0e
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,17 +4,16 @@ use std::error::Error; | |||||
| use std::fmt; | ||||||
| use std::future::Future; | ||||||
| use std::mem::MaybeUninit; | ||||||
| use std::ptr; | ||||||
| use std::sync::atomic::{AtomicBool, Ordering}; | ||||||
|
|
||||||
| /// A thread-safe cell which can be written to only once. | ||||||
| /// | ||||||
| /// Provides the functionality to either set the value, in case `OnceCell` | ||||||
| /// is uninitialized, or get the already initialized value by using an async | ||||||
| /// function via [`OnceCell::get_or_init_with`] or by using a Future via | ||||||
| /// [`OnceCell::get_or_init`] directly via [`OnceCell::get_or_init`]. | ||||||
| /// function via [`OnceCell::get_or_init_with`]. | ||||||
| /// | ||||||
| /// [`OnceCell::get_or_init_with`]: crate::sync::OnceCell::get_or_init_with | ||||||
| /// [`OnceCell::get_or_init`]: crate::sync::OnceCell::get_or_init | ||||||
| /// | ||||||
| /// # Examples | ||||||
| /// ``` | ||||||
|
|
@@ -24,7 +23,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; | |||||
| /// 1 + 1 | ||||||
| /// } | ||||||
| /// | ||||||
| /// static ONCE: OnceCell<u32> = OnceCell::new(); | ||||||
| /// static ONCE: OnceCell<u32> = OnceCell::const_new(); | ||||||
| /// | ||||||
| /// #[tokio::main] | ||||||
| /// async fn main() { | ||||||
|
|
@@ -76,10 +75,19 @@ impl<T: PartialEq> PartialEq for OnceCell<T> { | |||||
| impl<T: Eq> Eq for OnceCell<T> {} | ||||||
|
|
||||||
| impl<T> OnceCell<T> { | ||||||
| /// Creates a new uninitialized OnceCell instance. | ||||||
| pub fn new() -> Self { | ||||||
| OnceCell { | ||||||
| value_set: AtomicBool::new(false), | ||||||
| value: UnsafeCell::new(MaybeUninit::uninit()), | ||||||
| semaphore: Semaphore::new(1), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Creates a new uninitialized OnceCell instance. | ||||||
| #[cfg(all(feature = "parking_lot", not(all(loom, test)),))] | ||||||
| #[cfg_attr(docsrs, doc(cfg(feature = "parking_lot")))] | ||||||
| pub const fn new() -> Self { | ||||||
| pub const fn const_new() -> Self { | ||||||
| OnceCell { | ||||||
| value_set: AtomicBool::new(false), | ||||||
| value: UnsafeCell::new(MaybeUninit::uninit()), | ||||||
|
|
@@ -97,6 +105,12 @@ impl<T> OnceCell<T> { | |||||
| &*self.value.with(|ptr| (*ptr).as_ptr()) | ||||||
| } | ||||||
|
|
||||||
| // SAFETY: safe to call only once self.initialized() is true and since | ||||||
| // only one mutable reference can call this method | ||||||
| unsafe fn get_unchecked_mut(&mut self) -> &mut T { | ||||||
| &mut *self.value.with_mut(|ptr| (*ptr).as_mut_ptr()) | ||||||
| } | ||||||
|
|
||||||
| // SAFETY: safe to call only once a permit on the semaphore has been | ||||||
| // acquired | ||||||
| unsafe fn set_value(&self, value: T) { | ||||||
|
|
@@ -115,27 +129,38 @@ impl<T> OnceCell<T> { | |||||
| if self.initialized() { | ||||||
| Ok(unsafe { self.get_unchecked() }) | ||||||
| } else { | ||||||
| Err(NotInitializedError) | ||||||
| Err(NotInitializedError(())) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// If the cell is initialized, this method returns a mutable reference to its value, | ||||||
| /// otherwise returns [`NotInitializedError`]. | ||||||
| /// | ||||||
| /// [`NotInitializedError`]: crate::sync::NotInitializederror | ||||||
| pub fn get_mut(&mut self) -> Result<&mut T, NotInitializedError> { | ||||||
| if self.initialized() { | ||||||
| Ok(unsafe { self.get_unchecked_mut() }) | ||||||
| } else { | ||||||
| Err(NotInitializedError(())) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Sets the value of the OnceCell to the argument value. | ||||||
| /// | ||||||
| /// If the value of the OnceCell was already set prior to this call | ||||||
| /// or some other set is currently initializing the cell, then | ||||||
| /// [`AlreadyInitializedError`] is returned. In order to wait | ||||||
| /// for an ongoing initialization to finish, call [`OnceCell::get_or_init`] | ||||||
| /// or [`OnceCell::get_or_init_with`] instead. | ||||||
| /// then [`AlreadyInitializedError`] is returned. If another thread | ||||||
| /// is initializing the cell while this method is called, | ||||||
| /// ['InitializingError`] is returned. In order to wait | ||||||
| /// for an ongoing initialization to finish, call | ||||||
| /// [`OnceCell::get_or_init_with`] instead. | ||||||
| /// | ||||||
| /// [`AlreadyInitializedError`]: crate::sync::AlreadyInitializedError | ||||||
| /// ['OnceCell::get_or_init`]: crate::sync::OnceCell::get_or_init | ||||||
| /// [`InitializingError`]: crate::sync::InitializingError | ||||||
| /// ['OnceCell::get_or_init_with`]: crate::sync::OnceCell::get_or_init_with | ||||||
| pub fn set(&self, value: T) -> Result<(), AlreadyInitializedError> { | ||||||
| pub fn set(&self, value: T) -> Result<(), SetError> { | ||||||
| if !self.initialized() { | ||||||
| // After acquire().await we have either acquired a permit while self.value | ||||||
| // is still uninitialized, or another thread has intialized the value and | ||||||
| // closed the semaphore, in which case self.initialized is true and we | ||||||
| // don't set the value | ||||||
| // Another thread might be initializing the cell, in which case `try_acquire` will | ||||||
| // return an error | ||||||
| match self.semaphore.try_acquire() { | ||||||
| Ok(_permit) => { | ||||||
| if !self.initialized() { | ||||||
|
|
@@ -151,16 +176,30 @@ impl<T> OnceCell<T> { | |||||
| } | ||||||
| } | ||||||
| _ => { | ||||||
| // Couldn't acquire the permit, look if initializing process is already completed | ||||||
| if !self.initialized() { | ||||||
| panic!( | ||||||
| "couldn't acquire a permit even though OnceCell value is uninitialized." | ||||||
| ); | ||||||
| return Err(SetError::InitializingError(())); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| Err(AlreadyInitializedError) | ||||||
| Err(SetError::AlreadyInitializedError(())) | ||||||
| } | ||||||
|
|
||||||
| /// Tries to set the value of the cell, overwriting the previously set value, in case one is | ||||||
| /// available. If no value was previously set, this method has the same functionality has | ||||||
| /// [`OnceCell::set`]. | ||||||
| /// | ||||||
| /// [`OnceCell::set`]: crate::sync::OnceCell::set | ||||||
| pub fn set_mut(&mut self, value: T) -> Result<(), SetError> { | ||||||
Darksonn marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| if self.initialized() { | ||||||
| // SAFETY: Setting this value is safe because the mutable reference guarantees exclusivity | ||||||
| unsafe { self.set_value(value) }; | ||||||
| Ok(()) | ||||||
| } else { | ||||||
| self.set(value) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Tries to initialize the value of the OnceCell using the async function `f`. | ||||||
|
|
@@ -217,56 +256,25 @@ impl<T> OnceCell<T> { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Tries to initialize the value of the `OnceCell` using the the Future `f`. | ||||||
| /// If the value of the `OnceCell` was already initialized prior to this call, | ||||||
| /// a reference to that initialized value is returned. If some other thread | ||||||
| /// initiated the initialization prior to this call and the initialization | ||||||
| /// hasn't completed, this call waits until the initialization is finished. | ||||||
| /// | ||||||
| /// This will deadlock if `f` internally tries to initialize the cell itself. | ||||||
| pub async fn get_or_init<F>(&self, f: F) -> &T | ||||||
| where | ||||||
| F: Future<Output = T>, | ||||||
| { | ||||||
| /// Moves the value out of the cell and drops the cell afterwards. | ||||||
|
||||||
| /// Moves the value out of the cell and drops the cell afterwards. | |
| /// Moves the value out of the cell, destroying the cell in the process. |
Darksonn marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Darksonn marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
Contributor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a typo here. Also, please put a period at the end (also for the other method).
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.