diff options
Diffstat (limited to 'vendor/once_cell/src')
-rw-r--r-- | vendor/once_cell/src/lib.rs | 14 | ||||
-rw-r--r-- | vendor/once_cell/src/race.rs | 92 |
2 files changed, 102 insertions, 4 deletions
diff --git a/vendor/once_cell/src/lib.rs b/vendor/once_cell/src/lib.rs index 41313f736..83149ac4f 100644 --- a/vendor/once_cell/src/lib.rs +++ b/vendor/once_cell/src/lib.rs @@ -208,7 +208,6 @@ //! ``` //! use once_cell::sync::OnceCell; //! -//! #[derive(Debug)] //! pub struct LateInit<T> { cell: OnceCell<T> } //! //! impl<T> LateInit<T> { @@ -228,22 +227,24 @@ //! } //! } //! -//! #[derive(Default, Debug)] +//! #[derive(Default)] //! struct A<'a> { //! b: LateInit<&'a B<'a>>, //! } //! -//! #[derive(Default, Debug)] +//! #[derive(Default)] //! struct B<'a> { //! a: LateInit<&'a A<'a>> //! } //! +//! //! fn build_cycle() { //! let a = A::default(); //! let b = B::default(); //! a.b.init(&b); //! b.a.init(&a); -//! println!("{:?}", a.b.a.b.a); +//! +//! let _a = &a.b.a.b.a; //! } //! ``` //! @@ -315,6 +316,10 @@ //! //! No, but you can use [`async_once_cell`](https://crates.io/crates/async_once_cell) instead. //! +//! **Can I bring my own mutex?** +//! +//! There is [generic_once_cell](https://crates.io/crates/generic_once_cell) to allow just that. +//! //! # Related crates //! //! * [double-checked-cell](https://github.com/niklasf/double-checked-cell) @@ -323,6 +328,7 @@ //! * [mitochondria](https://crates.io/crates/mitochondria) //! * [lazy_static](https://crates.io/crates/lazy_static) //! * [async_once_cell](https://crates.io/crates/async_once_cell) +//! * [generic_once_cell](https://crates.io/crates/generic_once_cell) (bring your own mutex) //! //! Most of this crate's functionality is available in `std` in nightly Rust. //! See the [tracking issue](https://github.com/rust-lang/rust/issues/74465). diff --git a/vendor/once_cell/src/race.rs b/vendor/once_cell/src/race.rs index fd255c4c7..dff5847c8 100644 --- a/vendor/once_cell/src/race.rs +++ b/vendor/once_cell/src/race.rs @@ -25,6 +25,8 @@ use atomic_polyfill as atomic; use core::sync::atomic; use atomic::{AtomicUsize, Ordering}; +use core::cell::UnsafeCell; +use core::marker::PhantomData; use core::num::NonZeroUsize; /// A thread-safe cell which can be written to only once. @@ -172,6 +174,96 @@ impl OnceBool { } } +/// A thread-safe cell which can be written to only once. +pub struct OnceRef<'a, T> { + inner: OnceNonZeroUsize, + ghost: PhantomData<UnsafeCell<&'a T>>, +} + +// TODO: Replace UnsafeCell with SyncUnsafeCell once stabilized +unsafe impl<'a, T: Sync> Sync for OnceRef<'a, T> {} + +impl<'a, T> core::fmt::Debug for OnceRef<'a, T> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "OnceRef({:?})", self.inner) + } +} + +impl<'a, T> Default for OnceRef<'a, T> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, T> OnceRef<'a, T> { + /// Creates a new empty cell. + pub const fn new() -> OnceRef<'a, T> { + OnceRef { inner: OnceNonZeroUsize::new(), ghost: PhantomData } + } + + /// Gets a reference to the underlying value. + pub fn get(&self) -> Option<&'a T> { + self.inner.get().map(|ptr| unsafe { &*(ptr.get() as *const T) }) + } + + /// Sets the contents of this cell to `value`. + /// + /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was + /// full. + pub fn set(&self, value: &'a T) -> Result<(), ()> { + let ptr = NonZeroUsize::new(value as *const T as usize).unwrap(); + self.inner.set(ptr) + } + + /// Gets the contents of the cell, initializing it with `f` if the cell was + /// empty. + /// + /// If several threads concurrently run `get_or_init`, more than one `f` can + /// be called. However, all threads will return the same value, produced by + /// some `f`. + pub fn get_or_init<F>(&self, f: F) -> &'a T + where + F: FnOnce() -> &'a T, + { + let f = || NonZeroUsize::new(f() as *const T as usize).unwrap(); + let ptr = self.inner.get_or_init(f); + unsafe { &*(ptr.get() as *const T) } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// If several threads concurrently run `get_or_init`, more than one `f` can + /// be called. However, all threads will return the same value, produced by + /// some `f`. + pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&'a T, E> + where + F: FnOnce() -> Result<&'a T, E>, + { + let f = || f().map(|value| NonZeroUsize::new(value as *const T as usize).unwrap()); + let ptr = self.inner.get_or_try_init(f)?; + unsafe { Ok(&*(ptr.get() as *const T)) } + } + + /// ```compile_fail + /// use once_cell::race::OnceRef; + /// + /// let mut l = OnceRef::new(); + /// + /// { + /// let y = 2; + /// let mut r = OnceRef::new(); + /// r.set(&y).unwrap(); + /// core::mem::swap(&mut l, &mut r); + /// } + /// + /// // l now contains a dangling reference to y + /// eprintln!("uaf: {}", l.get().unwrap()); + /// ``` + fn _dummy() {} +} + #[cfg(feature = "alloc")] pub use self::once_box::OnceBox; |