Skip to content

Commit

Permalink
Merge #170
Browse files Browse the repository at this point in the history
170: Add `OnceCell::with_value` and improve `Clone` implementation r=matklad a=a1phyr

This PR adds `OnceCell::with_value` to both `sync::OnceCell` and `unsync::OnceCell`. This enables creating an initialized `OnceCell` at compile time and/or without synchronization cost.

Additionally, this improve `Clone` implementations by removing a panicking branch, reducing synchronization cost and overriding `clone_from` implementation.

Closes #164 

Co-authored-by: Benoît du Garreau <bdgdlm@outlook.com>
  • Loading branch information
bors[bot] and a1phyr committed Mar 3, 2022
2 parents c3a3ede + f35e353 commit 090caea
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 20 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog

## 1.11

- Add `OnceCell::with_value` to create initialized `OnceCell` at compile time.
- Improve `Clone` implementation for `OnceCell`.

## 1.10

- upgrade `parking_lot` to `0.12.0` (note that this bumps MSRV with `parking_lot` feature enabled to `1.49.0`).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -33,7 +33,7 @@ atomic-polyfill = { version = "0.1", optional = true }

[dev-dependencies]
lazy_static = "1.0.0"
crossbeam-utils = "0.7.2"
crossbeam-utils = "0.8.7"
regex = "1.2.0"

[features]
Expand Down
14 changes: 13 additions & 1 deletion src/imp_pl.rs
Expand Up @@ -35,6 +35,14 @@ impl<T> OnceCell<T> {
}
}

pub(crate) const fn with_value(value: T) -> OnceCell<T> {
OnceCell {
mutex: parking_lot::const_mutex(()),
is_initialized: AtomicBool::new(true),
value: UnsafeCell::new(Some(value)),
}
}

/// Safety: synchronizes with store to value via Release/Acquire.
#[inline]
pub(crate) fn is_initialized(&self) -> bool {
Expand Down Expand Up @@ -115,7 +123,11 @@ impl<T> OnceCell<T> {

// Note: this is intentionally monomorphic
#[inline(never)]
fn initialize_inner(mutex: &Mutex<()>, is_initialized: &AtomicBool, init: &mut dyn FnMut() -> bool) {
fn initialize_inner(
mutex: &Mutex<()>,
is_initialized: &AtomicBool,
init: &mut dyn FnMut() -> bool,
) {
let _guard = mutex.lock();

if !is_initialized.load(Ordering::Acquire) {
Expand Down
8 changes: 8 additions & 0 deletions src/imp_std.rs
Expand Up @@ -69,6 +69,14 @@ impl<T> OnceCell<T> {
}
}

pub(crate) const fn with_value(value: T) -> OnceCell<T> {
OnceCell {
state_and_queue: AtomicUsize::new(COMPLETE),
_marker: PhantomData,
value: UnsafeCell::new(Some(value)),
}
}

/// Safety: synchronizes with store to value via Release/(Acquire|SeqCst).
#[inline]
pub(crate) fn is_initialized(&self) -> bool {
Expand Down
50 changes: 32 additions & 18 deletions src/lib.rs
Expand Up @@ -399,14 +399,17 @@ pub mod unsync {

impl<T: Clone> Clone for OnceCell<T> {
fn clone(&self) -> OnceCell<T> {
let res = OnceCell::new();
if let Some(value) = self.get() {
match res.set(value.clone()) {
Ok(()) => (),
Err(_) => unreachable!(),
}
match self.get() {
Some(value) => OnceCell::with_value(value.clone()),
None => OnceCell::new(),
}
}

fn clone_from(&mut self, source: &Self) {
match (self.get_mut(), source.get()) {
(Some(this), Some(source)) => this.clone_from(source),
_ => *self = source.clone(),
}
res
}
}

Expand All @@ -420,7 +423,7 @@ pub mod unsync {

impl<T> From<T> for OnceCell<T> {
fn from(value: T) -> Self {
OnceCell { inner: UnsafeCell::new(Some(value)) }
OnceCell::with_value(value)
}
}

Expand All @@ -430,6 +433,11 @@ pub mod unsync {
OnceCell { inner: UnsafeCell::new(None) }
}

/// Creates a new initialized cell.
pub const fn with_value(value: T) -> OnceCell<T> {
OnceCell { inner: UnsafeCell::new(Some(value)) }
}

/// Gets a reference to the underlying value.
///
/// Returns `None` if the cell is empty.
Expand Down Expand Up @@ -810,22 +818,23 @@ pub mod sync {

impl<T: Clone> Clone for OnceCell<T> {
fn clone(&self) -> OnceCell<T> {
let res = OnceCell::new();
if let Some(value) = self.get() {
match res.set(value.clone()) {
Ok(()) => (),
Err(_) => unreachable!(),
}
match self.get() {
Some(value) => Self::with_value(value.clone()),
None => Self::new(),
}
}

fn clone_from(&mut self, source: &Self) {
match (self.get_mut(), source.get()) {
(Some(this), Some(source)) => this.clone_from(source),
_ => *self = source.clone(),
}
res
}
}

impl<T> From<T> for OnceCell<T> {
fn from(value: T) -> Self {
let cell = Self::new();
cell.get_or_init(|| value);
cell
Self::with_value(value)
}
}

Expand All @@ -843,6 +852,11 @@ pub mod sync {
OnceCell(Imp::new())
}

/// Creates a new initialized cell.
pub const fn with_value(value: T) -> OnceCell<T> {
OnceCell(Imp::with_value(value))
}

/// Gets the reference to the underlying value.
///
/// Returns `None` if the cell is empty, or being initialized. This
Expand Down
13 changes: 13 additions & 0 deletions tests/it.rs
Expand Up @@ -17,6 +17,13 @@ mod unsync {
assert_eq!(c.get(), Some(&92));
}

#[test]
fn once_cell_with_value() {
const CELL: OnceCell<i32> = OnceCell::with_value(12);
let cell = CELL;
assert_eq!(cell.get(), Some(&12));
}

#[test]
fn once_cell_get_mut() {
let mut c = OnceCell::new();
Expand Down Expand Up @@ -230,6 +237,12 @@ mod sync {
assert_eq!(c.get(), Some(&92));
}

#[test]
fn once_cell_with_value() {
static CELL: OnceCell<i32> = OnceCell::with_value(12);
assert_eq!(CELL.get(), Some(&12));
}

#[test]
fn once_cell_get_mut() {
let mut c = OnceCell::new();
Expand Down

0 comments on commit 090caea

Please sign in to comment.