-
-
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
Add OnceCell #3591
Changes from 11 commits
b3e521d
440a547
fdadf94
02dcffc
c539d68
9940f2c
3ca002f
b76ee23
1f8753a
7b1be1a
741b508
cdc3edc
a92ce09
b58dda5
201cbf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,341 @@ | ||||||
| use super::Semaphore; | ||||||
| use crate::loom::cell::UnsafeCell; | ||||||
| use std::error::Error; | ||||||
| use std::fmt; | ||||||
| use std::future::Future; | ||||||
| use std::mem::MaybeUninit; | ||||||
| use std::ops::Drop; | ||||||
| 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`]. | ||||||
| /// | ||||||
| /// [`OnceCell::get_or_init`]: crate::sync::OnceCell::get_or_init | ||||||
| /// | ||||||
| /// # Examples | ||||||
| /// ``` | ||||||
| /// use tokio::sync::OnceCell; | ||||||
| /// | ||||||
| /// async fn some_computation() -> u32 { | ||||||
| /// 1 + 1 | ||||||
| /// } | ||||||
| /// | ||||||
| /// static ONCE: OnceCell<u32> = OnceCell::const_new(); | ||||||
| /// | ||||||
| /// #[tokio::main] | ||||||
| /// async fn main() { | ||||||
| /// let result1 = tokio::spawn(async { | ||||||
| /// ONCE.get_or_init(some_computation).await | ||||||
| /// }).await.unwrap(); | ||||||
| /// assert_eq!(*result1, 2); | ||||||
| /// } | ||||||
| /// ``` | ||||||
| pub struct OnceCell<T> { | ||||||
| value_set: AtomicBool, | ||||||
| value: UnsafeCell<MaybeUninit<T>>, | ||||||
| semaphore: Semaphore, | ||||||
| } | ||||||
|
|
||||||
| impl<T> Default for OnceCell<T> { | ||||||
| fn default() -> OnceCell<T> { | ||||||
| OnceCell::new() | ||||||
| } | ||||||
| } | ||||||
Darksonn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| impl<T: fmt::Debug> fmt::Debug for OnceCell<T> { | ||||||
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
| fmt.debug_struct("OnceCell") | ||||||
| .field("value", &self.get()) | ||||||
| .finish() | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| impl<T: Clone> Clone for OnceCell<T> { | ||||||
| fn clone(&self) -> OnceCell<T> { | ||||||
| OnceCell::new_with(self.get().cloned()) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
Darksonn marked this conversation as resolved.
Show resolved
Hide resolved
Darksonn marked this conversation as resolved.
Show resolved
Hide resolved
Darksonn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| impl<T: PartialEq> PartialEq for OnceCell<T> { | ||||||
| fn eq(&self, other: &OnceCell<T>) -> bool { | ||||||
| self.get() == other.get() | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| impl<T: Eq> Eq for OnceCell<T> {} | ||||||
|
|
||||||
| impl<T> Drop for OnceCell<T> { | ||||||
| fn drop(&mut self) { | ||||||
| if self.initialized() { | ||||||
| unsafe { | ||||||
| self.value | ||||||
| .with_mut(|ptr| ptr::drop_in_place((&mut *ptr).as_mut_ptr())); | ||||||
| }; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| 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 initialized OnceCell instance if `value` is `Some`, otherwise | ||||||
| /// has the same functionality as [`OnceCell::new`]. | ||||||
| /// | ||||||
| /// [`OnceCell::new`]: crate::sync::OnceCell::new | ||||||
| pub fn new_with(value: Option<T>) -> Self { | ||||||
| if let Some(v) = value { | ||||||
| let semaphore = Semaphore::new(0); | ||||||
| semaphore.close(); | ||||||
| OnceCell { | ||||||
| value_set: AtomicBool::new(true), | ||||||
| value: UnsafeCell::new(MaybeUninit::new(v)), | ||||||
| semaphore, | ||||||
| } | ||||||
| } else { | ||||||
| OnceCell::new() | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// 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 const_new() -> Self { | ||||||
| OnceCell { | ||||||
| value_set: AtomicBool::new(false), | ||||||
| value: UnsafeCell::new(MaybeUninit::uninit()), | ||||||
| semaphore: Semaphore::const_new(1), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Whether the value of the OnceCell is set or not. | ||||||
| pub fn initialized(&self) -> bool { | ||||||
| self.value_set.load(Ordering::Acquire) | ||||||
| } | ||||||
|
|
||||||
| // SAFETY: safe to call only once self.initialized() is true | ||||||
| unsafe fn get_unchecked(&self) -> &T { | ||||||
| &*self.value.with(|ptr| (*ptr).as_ptr()) | ||||||
| } | ||||||
|
|
||||||
| // SAFETY: safe to call only once self.initialized() is true. Safe because | ||||||
| // because of the mutable reference. | ||||||
| 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) { | ||||||
| self.value.with_mut(|ptr| (*ptr).as_mut_ptr().write(value)); | ||||||
| self.value_set.store(true, Ordering::Release); | ||||||
| self.semaphore.close(); | ||||||
| } | ||||||
|
|
||||||
| /// Tries to get a reference to the value of the OnceCell. | ||||||
| /// | ||||||
| /// Returns None if the value of the OnceCell hasn't previously been initialized. | ||||||
| pub fn get(&self) -> Option<&T> { | ||||||
| if self.initialized() { | ||||||
| Some(unsafe { self.get_unchecked() }) | ||||||
| } else { | ||||||
| None | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Tries to return a mutable reference to the value of the cell. | ||||||
| /// | ||||||
| /// Returns None if the cell hasn't previously been initialized. | ||||||
| pub fn get_mut(&mut self) -> Option<&mut T> { | ||||||
| if self.initialized() { | ||||||
| Some(unsafe { self.get_unchecked_mut() }) | ||||||
| } else { | ||||||
| None | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Sets the value of the OnceCell to the argument value. | ||||||
| /// | ||||||
| /// If the value of the OnceCell was already set prior to this call | ||||||
| /// then [`SetError::AlreadyInitializedError`] is returned. If another thread | ||||||
| /// is initializing the cell while this method is called, | ||||||
| /// ['SetError::InitializingError`] is returned. In order to wait | ||||||
|
||||||
| /// ['SetError::InitializingError`] is returned. In order to wait | |
| /// [`SetError::InitializingError`] is returned. In order to wait |
Outdated
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.
| /// 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. |
Outdated
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).
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.
This makes the example a bit simpler.