Upgrade rust/crates/once_cell to 1.4.0
Test: None
Change-Id: Ib5242a48f281cb07f5a42e8b430be7edfd421deb
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 19a4ba4..262cf9e 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "ea4634cef717e3d57b456460557426fc79cd4cbb"
+ "sha1": "311c33480fbd8b56b0d3db081a0e328b199e2b9c"
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eaaf4bd..02fe9ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## 1.4.0
+
+- upgrade `parking_lot` to `0.10` (note that this bumps MSRV with `parking_lot` feature enabled to `1.36.0`).
+- add `OnceCell::take`.
+- upgrade crossbeam utils (private dependency) to `0.7`.
+
## 1.3.1
- remove unnecessary `F: fmt::Debug` bound from `impl fmt::Debug for Lazy<T, F>`.
diff --git a/Cargo.toml b/Cargo.toml
index ceea45c..e2c2279 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "once_cell"
-version = "1.3.1"
+version = "1.4.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
exclude = ["*.png", "*.svg", "/Cargo.lock.min", "/.travis.yml", "/run-miri-tests.sh", "rustfmt.toml"]
description = "Single assignment cells and lazy values."
@@ -52,11 +52,11 @@
name = "test_synchronization"
required-features = ["std"]
[dependencies.parking_lot]
-version = "0.9.0"
+version = "0.10.0"
optional = true
default_features = false
[dev-dependencies.crossbeam-utils]
-version = "0.6.0"
+version = "0.7.2"
[dev-dependencies.lazy_static]
version = "1.0.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index fa28791..f11573e 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "once_cell"
-version = "1.3.1"
+version = "1.4.0"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2018"
@@ -19,11 +19,11 @@
# Uses parking_lot to implement once_cell::sync::OnceCell.
# This makes not speed difference, but makes each OnceCell<T>
# for up to two bytes smaller, depending on the size of the T.
-parking_lot = { version = "0.9.0", optional = true, default_features = false }
+parking_lot = { version = "0.10.0", optional = true, default_features = false }
[dev-dependencies]
lazy_static = "1.0.0"
-crossbeam-utils = "0.6.0"
+crossbeam-utils = "0.7.2"
regex = "1.2.0"
[features]
diff --git a/METADATA b/METADATA
index befd7dc..bf87c4a 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
type: GIT
value: "https://github.com/matklad/once_cell"
}
- version: "1.3.1"
+ version: "1.4.0"
license_type: NOTICE
last_upgrade_date {
year: 2020
month: 5
- day: 11
+ day: 18
}
}
diff --git a/examples/bench_acquire.rs b/examples/bench_acquire.rs
index da539a7..1518047 100644
--- a/examples/bench_acquire.rs
+++ b/examples/bench_acquire.rs
@@ -1,6 +1,6 @@
-/// Benchmark the overhead that the synchronization of `OnceCell::get` causes.
-/// We do some other operations that write to memory to get an imprecise but somewhat realistic
-/// measurement.
+//! Benchmark the overhead that the synchronization of `OnceCell::get` causes.
+//! We do some other operations that write to memory to get an imprecise but somewhat realistic
+//! measurement.
use once_cell::sync::OnceCell;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -29,7 +29,7 @@
let mut data = [i; 128];
let mut accum = 0usize;
for _ in 0..N_ROUNDS {
- let _value = CELL.get_or_init(|| i+1);
+ let _value = CELL.get_or_init(|| i + 1);
let k = OTHER.fetch_add(data[accum & 0x7F] as usize, Ordering::Relaxed);
for j in data.iter_mut() {
*j = (*j).wrapping_add(accum);
diff --git a/examples/test_synchronization.rs b/examples/test_synchronization.rs
index 1438bf3..0d54f98 100644
--- a/examples/test_synchronization.rs
+++ b/examples/test_synchronization.rs
@@ -1,11 +1,11 @@
-/// Test if the OnceCell properly synchronizes.
-/// Needs to be run in release mode.
-///
-/// We create a `Vec` with `N_ROUNDS` of `OnceCell`s. All threads will walk the `Vec`, and race to
-/// be the first one to initialize a cell.
-/// Every thread adds the results of the cells it sees to an accumulator, which is compared at the
-/// end.
-/// All threads should end up with the same result.
+//! Test if the OnceCell properly synchronizes.
+//! Needs to be run in release mode.
+//!
+//! We create a `Vec` with `N_ROUNDS` of `OnceCell`s. All threads will walk the `Vec`, and race to
+//! be the first one to initialize a cell.
+//! Every thread adds the results of the cells it sees to an accumulator, which is compared at the
+//! end.
+//! All threads should end up with the same result.
use once_cell::sync::OnceCell;
diff --git a/src/imp_pl.rs b/src/imp_pl.rs
index 3c5b1ec..5885e77 100644
--- a/src/imp_pl.rs
+++ b/src/imp_pl.rs
@@ -1,6 +1,8 @@
use std::{
cell::UnsafeCell,
+ mem::{self, MaybeUninit},
panic::{RefUnwindSafe, UnwindSafe},
+ ptr,
sync::atomic::{AtomicBool, Ordering},
};
@@ -9,7 +11,7 @@
pub(crate) struct OnceCell<T> {
mutex: Mutex,
is_initialized: AtomicBool,
- pub(crate) value: UnsafeCell<Option<T>>,
+ value: UnsafeCell<MaybeUninit<T>>,
}
// Why do we need `T: Send`?
@@ -28,7 +30,7 @@
OnceCell {
mutex: Mutex::new(),
is_initialized: AtomicBool::new(false),
- value: UnsafeCell::new(None),
+ value: UnsafeCell::new(MaybeUninit::uninit()),
}
}
@@ -56,13 +58,76 @@
// - finally, if it returns Ok, we store the value and store the flag with
// `Release`, which synchronizes with `Acquire`s.
let value = f()?;
- let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
- debug_assert!(slot.is_none());
- *slot = Some(value);
+ // Safe b/c we have a unique access and no panic may happen
+ // until the cell is marked as initialized.
+ unsafe { self.as_mut_ptr().write(value) };
self.is_initialized.store(true, Ordering::Release);
}
Ok(())
}
+
+ /// Get the reference to the underlying value, without checking if the cell
+ /// is initialized.
+ ///
+ /// # Safety
+ ///
+ /// Caller must ensure that the cell is in initialized state, and that
+ /// the contents are acquired by (synchronized to) this thread.
+ pub(crate) unsafe fn get_unchecked(&self) -> &T {
+ debug_assert!(self.is_initialized());
+ &*self.as_ptr()
+ }
+
+ /// Gets the mutable reference to the underlying value.
+ /// Returns `None` if the cell is empty.
+ pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
+ if self.is_initialized() {
+ // Safe b/c we have a unique access and value is initialized.
+ Some(unsafe { &mut *self.as_mut_ptr() })
+ } else {
+ None
+ }
+ }
+
+ /// Consumes this `OnceCell`, returning the wrapped value.
+ /// Returns `None` if the cell was empty.
+ pub(crate) fn into_inner(self) -> Option<T> {
+ if !self.is_initialized() {
+ return None;
+ }
+
+ // Safe b/c we have a unique access and value is initialized.
+ let value: T = unsafe { ptr::read(self.as_ptr()) };
+
+ // It's OK to `mem::forget` without dropping, because both `self.mutex`
+ // and `self.is_initialized` are not heap-allocated.
+ mem::forget(self);
+
+ Some(value)
+ }
+
+ fn as_ptr(&self) -> *const T {
+ unsafe {
+ let slot: &MaybeUninit<T> = &*self.value.get();
+ slot.as_ptr()
+ }
+ }
+
+ fn as_mut_ptr(&self) -> *mut T {
+ unsafe {
+ let slot: &mut MaybeUninit<T> = &mut *self.value.get();
+ slot.as_mut_ptr()
+ }
+ }
+}
+
+impl<T> Drop for OnceCell<T> {
+ fn drop(&mut self) {
+ if self.is_initialized() {
+ // Safe b/c we have a unique access and value is initialized.
+ unsafe { ptr::drop_in_place(self.as_mut_ptr()) };
+ }
+ }
}
/// Wrapper around parking_lot's `RawMutex` which has `const fn` new.
@@ -92,9 +157,8 @@
}
#[test]
-#[cfg(target_pointer_width = "64")]
fn test_size() {
use std::mem::size_of;
- assert_eq!(size_of::<OnceCell<u32>>(), 3 * size_of::<u32>());
+ assert_eq!(size_of::<OnceCell<bool>>(), 2 * size_of::<bool>() + size_of::<u8>());
}
diff --git a/src/imp_std.rs b/src/imp_std.rs
index f5a6ea5..bb076a6 100644
--- a/src/imp_std.rs
+++ b/src/imp_std.rs
@@ -5,6 +5,7 @@
use std::{
cell::{Cell, UnsafeCell},
+ hint::unreachable_unchecked,
marker::PhantomData,
panic::{RefUnwindSafe, UnwindSafe},
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
@@ -21,7 +22,7 @@
// that far. It was stabilized in 1.36.0, so, if you are reading this and
// it's higher than 1.46.0 outside, please send a PR! ;) (and do the same
// for `Lazy`, while we are at it).
- pub(crate) value: UnsafeCell<Option<T>>,
+ value: UnsafeCell<Option<T>>,
}
// Why do we need `T: Send`?
@@ -90,22 +91,59 @@
{
let mut f = Some(f);
let mut res: Result<(), E> = Ok(());
- let slot = &self.value;
+ let slot: *mut Option<T> = self.value.get();
initialize_inner(&self.state_and_queue, &mut || {
let f = f.take().unwrap();
match f() {
Ok(value) => {
- unsafe { *slot.get() = Some(value) };
+ unsafe { *slot = Some(value) };
true
}
- Err(e) => {
- res = Err(e);
+ Err(err) => {
+ res = Err(err);
false
}
}
});
res
}
+
+ /// Get the reference to the underlying value, without checking if the cell
+ /// is initialized.
+ ///
+ /// # Safety
+ ///
+ /// Caller must ensure that the cell is in initialized state, and that
+ /// the contents are acquired by (synchronized to) this thread.
+ pub(crate) unsafe fn get_unchecked(&self) -> &T {
+ debug_assert!(self.is_initialized());
+ let slot: &Option<T> = &*self.value.get();
+ match slot {
+ Some(value) => value,
+ // This unsafe does improve performance, see `examples/bench`.
+ None => {
+ debug_assert!(false);
+ unreachable_unchecked()
+ }
+ }
+ }
+
+ /// Gets the mutable reference to the underlying value.
+ /// Returns `None` if the cell is empty.
+ pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
+ // Safe b/c we have a unique access.
+ unsafe { &mut *self.value.get() }.as_mut()
+ }
+
+ /// Consumes this `OnceCell`, returning the wrapped value.
+ /// Returns `None` if the cell was empty.
+ #[inline]
+ pub(crate) fn into_inner(self) -> Option<T> {
+ // Because `into_inner` takes `self` by value, the compiler statically
+ // verifies that it is not currently borrowed.
+ // So, it is safe to move out `Option<T>`.
+ self.value.into_inner()
+ }
}
// Corresponds to `std::sync::Once::call_inner`
diff --git a/src/lib.rs b/src/lib.rs
index e7bd7f6..90a3c6b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,9 @@
/*!
# Overview
-`once_cell` provides two new cell-like types, `unsync::OnceCell` and `sync::OnceCell`. `OnceCell`
-might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access
-to the stored contents. In a nutshell, API looks *roughly* like this:
+`once_cell` provides two new cell-like types, [`unsync::OnceCell`] and [`sync::OnceCell`]. A `OnceCell`
+might store arbitrary non-`Copy` types, can be assigned to at most once and provides direct access
+to the stored contents. In a nutshell, the API looks *roughly* like this:
```rust,ignore
impl<T> OnceCell<T> {
@@ -13,12 +13,16 @@
}
```
-Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference.
-Because of the single assignment restriction `get` can return an `&T` instead of `Ref<T>`
+Note that, like with [`RefCell`] and [`Mutex`], the `set` method requires only a shared reference.
+Because of the single assignment restriction `get` can return a `&T` instead of `Ref<T>`
or `MutexGuard<T>`.
-The `sync` flavor is thread-safe (that is, implements [`Sync`]) trait, while the `unsync` one is not.
+The `sync` flavor is thread-safe (that is, implements the [`Sync`] trait), while the `unsync` one is not.
+[`unsync::OnceCell`]: unsync/struct.OnceCell.html
+[`sync::OnceCell`]: sync/struct.ONceCell.html
+[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
+[`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
# Patterns
@@ -58,7 +62,7 @@
## Lazy initialized global data
-This is essentially `lazy_static!` macro, but without a macro.
+This is essentially the `lazy_static!` macro, but without a macro.
```rust
use std::{sync::Mutex, collections::HashMap};
@@ -76,7 +80,7 @@
}
```
-There are also `sync::Lazy` and `unsync::Lazy` convenience types to streamline this pattern:
+There are also the [`sync::Lazy`] and [`unsync::Lazy`] convenience types to streamline this pattern:
```rust
use std::{sync::Mutex, collections::HashMap};
@@ -94,6 +98,9 @@
}
```
+[`sync::Lazy`]: sync/struct.Lazy.html
+[`unsync::Lazy`]: unsync/struct.Lazy.html
+
## General purpose lazy evaluation
Unlike `lazy_static!`, `Lazy` works with local variables.
@@ -148,7 +155,7 @@
}
```
-This macro can be useful to avoid "compile regex on every loop iteration" problem.
+This macro can be useful to avoid the "compile regex on every loop iteration" problem.
# Comparison with std
@@ -170,24 +177,27 @@
# Minimum Supported `rustc` Version
-This crate's minimum supported `rustc` version is `1.31.1`.
+This crate's minimum supported `rustc` version is `1.31.1` (or `1.36.0` with the
+`parking_lot` feature enabled).
-If only `std` feature is enabled, MSRV will be updated conservatively.
+If only the `std` feature is enabled, MSRV will be updated conservatively.
When using other features, like `parking_lot`, MSRV might be updated more frequently, up to the latest stable.
In both cases, increasing MSRV is *not* considered a semver-breaking change.
# Implementation details
-Implementation is based on [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/)
-and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and `std::sync::Once`. In some sense,
+The implementation is based on the [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/)
+and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and [`std::sync::Once`]. In some sense,
`once_cell` just streamlines and unifies those APIs.
To implement a sync flavor of `OnceCell`, this crates uses either a custom re-implementation of
`std::sync::Once` or `parking_lot::Mutex`. This is controlled by the `parking_lot` feature, which
-is enabled by default. Performance is the same for both cases, but `parking_lot` based `OnceCell<T>`
+is enabled by default. Performance is the same for both cases, but the `parking_lot` based `OnceCell<T>`
is smaller by up to 16 bytes.
-This crate uses unsafe.
+This crate uses `unsafe`.
+
+[`std::sync::Once`]: https://doc.rust-lang.org/std/sync/struct.Once.html
# F.A.Q.
@@ -202,7 +212,7 @@
`lazy_static` has received significantly more real world testing, but `once_cell` is also a widely
used crate.
-**Should I use sync or unsync flavor?**
+**Should I use the sync or unsync flavor?**
Because Rust compiler checks thread safety for you, it's impossible to accidentally use `unsync` where
`sync` is required. So, use `unsync` in single-threaded code and `sync` in multi-threaded. It's easy
@@ -236,18 +246,20 @@
pub mod unsync {
use core::{
cell::{Cell, UnsafeCell},
- fmt,
+ fmt, mem,
ops::{Deref, DerefMut},
};
#[cfg(feature = "std")]
use std::panic::{RefUnwindSafe, UnwindSafe};
- /// A cell which can be written to only once. Not thread safe.
+ /// A cell which can be written to only once. It is not thread safe.
///
- /// Unlike `:td::cell::RefCell`, a `OnceCell` provides simple `&`
+ /// Unlike [`std::cell::RefCell`], a `OnceCell` provides simple `&`
/// references to the contents.
///
+ /// [`std::cell::RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
+ ///
/// # Example
/// ```
/// use once_cell::unsync::OnceCell;
@@ -323,7 +335,7 @@
OnceCell { inner: UnsafeCell::new(None) }
}
- /// Gets the reference to the underlying value.
+ /// Gets a reference to the underlying value.
///
/// Returns `None` if the cell is empty.
pub fn get(&self) -> Option<&T> {
@@ -331,7 +343,7 @@
unsafe { &*self.inner.get() }.as_ref()
}
- /// Gets the mutable reference to the underlying value.
+ /// Gets a mutable reference to the underlying value.
///
/// Returns `None` if the cell is empty.
pub fn get_mut(&mut self) -> Option<&mut T> {
@@ -443,6 +455,27 @@
Ok(self.get().unwrap())
}
+ /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state.
+ ///
+ /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use once_cell::unsync::OnceCell;
+ ///
+ /// let mut cell: OnceCell<String> = OnceCell::new();
+ /// assert_eq!(cell.take(), None);
+ ///
+ /// let mut cell = OnceCell::new();
+ /// cell.set("hello".to_string()).unwrap();
+ /// assert_eq!(cell.take(), Some("hello".to_string()));
+ /// assert_eq!(cell.get(), None);
+ /// ```
+ pub fn take(&mut self) -> Option<T> {
+ mem::replace(self, Self::default()).into_inner()
+ }
+
/// Consumes the `OnceCell`, returning the wrapped value.
///
/// Returns `None` if the cell was empty.
@@ -569,8 +602,7 @@
pub mod sync {
use std::{
cell::Cell,
- fmt,
- hint::unreachable_unchecked,
+ fmt, mem,
ops::{Deref, DerefMut},
panic::RefUnwindSafe,
};
@@ -663,7 +695,7 @@
/// method never blocks.
pub fn get(&self) -> Option<&T> {
if self.0.is_initialized() {
- // Safe b/c checked is_initialize
+ // Safe b/c value is initialized.
Some(unsafe { self.get_unchecked() })
} else {
None
@@ -674,28 +706,18 @@
///
/// Returns `None` if the cell is empty.
pub fn get_mut(&mut self) -> Option<&mut T> {
- // Safe b/c we have a unique access.
- unsafe { &mut *self.0.value.get() }.as_mut()
+ self.0.get_mut()
}
/// Get the reference to the underlying value, without checking if the
/// cell is initialized.
///
- /// Safety:
+ /// # Safety
///
/// Caller must ensure that the cell is in initialized state, and that
/// the contents are acquired by (synchronized to) this thread.
pub unsafe fn get_unchecked(&self) -> &T {
- debug_assert!(self.0.is_initialized());
- let slot: &Option<T> = &*self.0.value.get();
- match slot {
- Some(value) => value,
- // This unsafe does improve performance, see `examples/bench`.
- None => {
- debug_assert!(false);
- unreachable_unchecked()
- }
- }
+ self.0.get_unchecked()
}
/// Sets the contents of this cell to `value`.
@@ -704,6 +726,7 @@
/// full.
///
/// # Example
+ ///
/// ```
/// use once_cell::sync::OnceCell;
///
@@ -802,11 +825,32 @@
}
self.0.initialize(f)?;
- // Safe b/c called initialize
+ // Safe b/c value is initialized.
debug_assert!(self.0.is_initialized());
Ok(unsafe { self.get_unchecked() })
}
+ /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state.
+ ///
+ /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use once_cell::sync::OnceCell;
+ ///
+ /// let mut cell: OnceCell<String> = OnceCell::new();
+ /// assert_eq!(cell.take(), None);
+ ///
+ /// let mut cell = OnceCell::new();
+ /// cell.set("hello".to_string()).unwrap();
+ /// assert_eq!(cell.take(), Some("hello".to_string()));
+ /// assert_eq!(cell.get(), None);
+ /// ```
+ pub fn take(&mut self) -> Option<T> {
+ mem::replace(self, Self::default()).into_inner()
+ }
+
/// Consumes the `OnceCell`, returning the wrapped value. Returns
/// `None` if the cell was empty.
///
@@ -823,17 +867,16 @@
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
/// ```
pub fn into_inner(self) -> Option<T> {
- // Because `into_inner` takes `self` by value, the compiler statically verifies
- // that it is not currently borrowed. So it is safe to move out `Option<T>`.
- self.0.value.into_inner()
+ self.0.into_inner()
}
}
/// A value which is initialized on the first access.
///
- /// This type is thread-safe and can be used in statics:
+ /// This type is thread-safe and can be used in statics.
///
/// # Example
+ ///
/// ```
/// use std::collections::HashMap;
///
@@ -893,7 +936,7 @@
impl<T, F: FnOnce() -> T> Lazy<T, F> {
/// Forces the evaluation of this lazy value and
- /// returns a reference to result. This is equivalent
+ /// returns a reference to the result. This is equivalent
/// to the `Deref` impl, but is explicit.
///
/// # Example
diff --git a/tests/test.rs b/tests/test.rs
index 7cd085d..c1351a7 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -448,7 +448,6 @@
}
#[test]
- #[cfg_attr(miri, ignore)] // leaks memory
fn static_lazy() {
static XS: Lazy<Vec<i32>> = Lazy::new(|| {
let mut xs = Vec::new();
@@ -467,7 +466,6 @@
}
#[test]
- #[cfg_attr(miri, ignore)] // leaks memory
fn static_lazy_via_fn() {
fn xs() -> &'static Vec<i32> {
static XS: OnceCell<Vec<i32>> = OnceCell::new();