Skip to content
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 once_cell::race::OnceRef #213

Merged
merged 1 commit into from Dec 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,10 @@

-

## 1.17.0

- Add `race::OnceRef` for storing a `&'a T`.

## 1.16.0

- Add `no_std` implementation based on `critical-section`,
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "once_cell"
version = "1.16.0"
version = "1.17.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
Expand Down
92 changes: 92 additions & 0 deletions src/race.rs
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down