Skip to content

Commit

Permalink
Add try insert
Browse files Browse the repository at this point in the history
  • Loading branch information
matklad committed May 29, 2021
1 parent 70200fa commit 500dc2a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog

## 1.8.0

- Add `try_insert` API -- a version of `set` that returns a reference.

## 1.7.2

- Improve code size when using parking_lot feature.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "once_cell"
version = "1.7.2"
version = "1.8.0-pre.1"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2018"
Expand Down
63 changes: 55 additions & 8 deletions src/lib.rs
Expand Up @@ -337,10 +337,11 @@ mod imp;
#[path = "imp_std.rs"]
mod imp;

/// Single-threaded version of `OnceCell`.
pub mod unsync {
use core::{
cell::{Cell, UnsafeCell},
fmt, mem,
fmt, hint, mem,
ops::{Deref, DerefMut},
};

Expand Down Expand Up @@ -463,17 +464,40 @@ pub mod unsync {
/// assert!(cell.get().is_some());
/// ```
pub fn set(&self, value: T) -> Result<(), T> {
let slot = unsafe { &*self.inner.get() };
if slot.is_some() {
return Err(value);
match self.try_insert(value) {
Ok(_) => Ok(()),
Err((_, value)) => Err(value),
}
}

/// Like [`set`](Self::set), but also returns a referce to the final cell value.
///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert!(cell.get().is_none());
///
/// assert_eq!(cell.try_insert(92), Ok(&92));
/// assert_eq!(cell.try_insert(62), Err((&92, 62)));
///
/// assert!(cell.get().is_some());
/// ```
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
if let Some(old) = self.get() {
return Err((old, value));
}
let slot = unsafe { &mut *self.inner.get() };
// This is the only place where we set the slot, no races
// due to reentrancy/concurrency are possible, and we've
// checked that slot is currently `None`, so this write
// maintains the `inner`'s invariant.
*slot = Some(value);
Ok(())
Ok(match &*slot {
Some(value) => value,
None => unsafe { hint::unreachable_unchecked() },
})
}

/// Gets the contents of the cell, initializing it with `f`
Expand Down Expand Up @@ -703,6 +727,7 @@ pub mod unsync {
}
}

/// Thread-safe, blocking version of `OnceCell`.
#[cfg(feature = "std")]
pub mod sync {
use std::{
Expand Down Expand Up @@ -849,11 +874,33 @@ pub mod sync {
/// }
/// ```
pub fn set(&self, value: T) -> Result<(), T> {
match self.try_insert(value) {
Ok(_) => Ok(()),
Err((_, value)) => Err(value),
}
}

/// Like [`set`](Self::set), but also returns a reference to the final cell value.
///
/// # Example
///
/// ```
/// use once_cell::unsync::OnceCell;
///
/// let cell = OnceCell::new();
/// assert!(cell.get().is_none());
///
/// assert_eq!(cell.try_insert(92), Ok(&92));
/// assert_eq!(cell.try_insert(62), Err((&92, 62)));
///
/// assert!(cell.get().is_some());
/// ```
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
let mut value = Some(value);
self.get_or_init(|| value.take().unwrap());
let res = self.get_or_init(|| value.take().unwrap());
match value {
None => Ok(()),
Some(value) => Err(value),
None => Ok(res),
Some(value) => Err((res, value)),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/race.rs
@@ -1,4 +1,4 @@
//! "First one wins" flavor of `OnceCell`.
//! Thread-safe, non-blocking, "first one wins" flavor of `OnceCell`.
//!
//! If two threads race to initialize a type from the `race` module, they
//! don't block, execute initialization function together, but only one of
Expand Down

0 comments on commit 500dc2a

Please sign in to comment.