diff --git a/CHANGELOG.md b/CHANGELOG.md index 005ce69..377b9a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ - +## 1.16.0-pre.1 + +- Add `no_std` implementation based on `critical-section` +- Deprecate `atomic-polyfill` feature (use the new `critical-section` instead) + ## 1.15.0 - Increase minimal supported Rust version to 1.56.0. diff --git a/Cargo.toml b/Cargo.toml index 1ad618f..228ce1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "once_cell" -version = "1.15.0" +version = "1.16.0-pre.1" authors = ["Aleksey Kladov "] license = "MIT OR Apache-2.0" edition = "2021" @@ -20,19 +20,10 @@ exclude = ["*.png", "*.svg", "/Cargo.lock.msrv", "rustfmt.toml"] members = ["xtask"] [dependencies] -# Uses parking_lot to implement once_cell::sync::OnceCell. -# This makes no speed difference, but makes each OnceCell -# up to 16 bytes smaller, depending on the size of the T. +# These optional dependencies are considered private impl details, +# only features from `[features]` table are a part of semver-guarded API. parking_lot_core = { version = "0.9.3", optional = true, default_features = false } - -# To be used in order to enable the race feature on targets -# that do not have atomics -# *Warning:* This can be unsound. Please read the README of -# [atomic-polyfill](https://github.com/embassy-rs/atomic-polyfill) -# and make sure you understand all the implications -atomic-polyfill = { version = "1", optional = true } - -# Uses `critical-section` to implement `once_cell::sync::OnceCell`. +atomic_polyfill = { package = "atomic-polyfill", version = "1", optional = true } critical_section = { package = "critical-section", version = "1", optional = true } [dev-dependencies] @@ -43,21 +34,32 @@ critical_section = { package = "critical-section", version = "1.1.1", features = [features] default = ["std"] + # Enables `once_cell::sync` module. -std = ["alloc", "sync"] +std = ["alloc"] + # Enables `once_cell::race::OnceBox` type. alloc = ["race"] + # Enables `once_cell::race` module. race = [] + +# Uses parking_lot to implement once_cell::sync::OnceCell. +# This makes no speed difference, but makes each OnceCell +# up to 16 bytes smaller, depending on the size of the T. +parking_lot = ["parking_lot_core"] + +# Uses `critical-section` to implement `sync` and `race` modules. in +# `#![no_std]` mode. Please read `critical-section` docs carefully +# before enabling this feature. +critical-section = ["critical_section", "atomic_polyfill" ] + # Enables semver-exempt APIs of this crate. # At the moment, this feature is unused. unstable = [] -sync = [] - -parking_lot = ["parking_lot_core"] - -critical-section = ["critical_section", "atomic-polyfill", "sync"] +# Only for backwards compatibility. +atomic-polyfill = ["critical-section"] [[example]] name = "bench" diff --git a/src/imp_cs.rs b/src/imp_cs.rs index 2548f58..668f18e 100644 --- a/src/imp_cs.rs +++ b/src/imp_cs.rs @@ -63,7 +63,7 @@ impl OnceCell { pub(crate) unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); // SAFETY: The caller ensures that the value is initialized and access synchronized. - self.value.borrow(CriticalSection::new()).get_unchecked() + crate::unwrap_unchecked(self.value.borrow(CriticalSection::new()).get()) } #[inline] diff --git a/src/imp_pl.rs b/src/imp_pl.rs index b723587..84d8593 100644 --- a/src/imp_pl.rs +++ b/src/imp_pl.rs @@ -58,7 +58,7 @@ impl OnceCell { // but that is more complicated // - finally, if it returns Ok, we store the value and store the flag with // `Release`, which synchronizes with `Acquire`s. - let f = unsafe { crate::take_unchecked(&mut f) }; + let f = unsafe { crate::unwrap_unchecked(f.take()) }; match f() { Ok(value) => unsafe { // Safe b/c we have a unique access and no panic may happen diff --git a/src/imp_std.rs b/src/imp_std.rs index f828d45..5761f01 100644 --- a/src/imp_std.rs +++ b/src/imp_std.rs @@ -78,7 +78,7 @@ impl OnceCell { initialize_or_wait( &self.queue, Some(&mut || { - let f = unsafe { crate::take_unchecked(&mut f) }; + let f = unsafe { crate::unwrap_unchecked(f.take()) }; match f() { Ok(value) => { unsafe { *slot = Some(value) }; diff --git a/src/lib.rs b/src/lib.rs index f44bc4c..448989c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -474,18 +474,6 @@ pub mod unsync { unsafe { &mut *self.inner.get() }.as_mut() } - /// 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. - #[cfg(feature = "critical-section")] - #[inline] - pub(crate) unsafe fn get_unchecked(&self) -> &T { - crate::unwrap_unchecked(self.get()) - } - /// Sets the contents of this cell to `value`. /// /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was @@ -830,7 +818,7 @@ pub mod unsync { } /// Thread-safe, blocking version of `OnceCell`. -#[cfg(feature = "sync")] +#[cfg(any(feature = "std", feature = "critical-section"))] pub mod sync { use core::{ cell::Cell, @@ -839,7 +827,7 @@ pub mod sync { panic::RefUnwindSafe, }; - use super::{imp::OnceCell as Imp, take_unchecked}; + use super::{imp::OnceCell as Imp, unwrap_unchecked}; /// A thread-safe cell which can be written to only once. /// @@ -960,7 +948,7 @@ pub mod sync { /// assert_eq!(*value, 92); /// t.join().unwrap(); /// ``` - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] pub fn wait(&self) -> &T { if !self.0.is_initialized() { self.0.wait() @@ -1050,7 +1038,7 @@ pub mod sync { /// ``` pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> { let mut value = Some(value); - let res = self.get_or_init(|| unsafe { take_unchecked(&mut value) }); + let res = self.get_or_init(|| unsafe { unwrap_unchecked(value.take()) }); match value { None => Ok(res), Some(value) => Err((res, value)), @@ -1377,12 +1365,7 @@ pub mod sync { #[cfg(feature = "race")] pub mod race; -#[cfg(feature = "sync")] -#[inline] -unsafe fn take_unchecked(val: &mut Option) -> T { - unwrap_unchecked(val.take()) -} - +// Remove once MSRV is at least 1.58. #[inline] unsafe fn unwrap_unchecked(val: Option) -> T { match val { diff --git a/src/race.rs b/src/race.rs index 28acf62..fd255c4 100644 --- a/src/race.rs +++ b/src/race.rs @@ -19,9 +19,9 @@ //! `Acquire` and `Release` have very little performance overhead on most //! architectures versus `Relaxed`. -#[cfg(feature = "atomic-polyfill")] +#[cfg(feature = "critical-section")] use atomic_polyfill as atomic; -#[cfg(not(feature = "atomic-polyfill"))] +#[cfg(not(feature = "critical-section"))] use core::sync::atomic; use atomic::{AtomicUsize, Ordering}; diff --git a/tests/it.rs b/tests/it.rs index 118be00..d18f0a1 100644 --- a/tests/it.rs +++ b/tests/it.rs @@ -249,14 +249,14 @@ mod unsync { } } -#[cfg(feature = "sync")] +#[cfg(any(feature = "std", feature = "critical-section"))] mod sync { use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] use std::sync::Barrier; - #[cfg(feature = "critical-section")] + #[cfg(not(feature = "std"))] use core::cell::Cell; use crossbeam_utils::thread::scope; @@ -360,7 +360,7 @@ mod sync { assert_eq!(cell.get(), Some(&"hello".to_string())); } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn wait() { let cell: OnceCell = OnceCell::new(); @@ -372,7 +372,7 @@ mod sync { .unwrap(); } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn get_or_init_stress() { let n_threads = if cfg!(miri) { 30 } else { 1_000 }; @@ -437,7 +437,7 @@ mod sync { #[test] #[cfg_attr(miri, ignore)] // miri doesn't support processes - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] fn reentrant_init() { let examples_dir = { let mut exe = std::env::current_exe().unwrap(); @@ -465,7 +465,7 @@ mod sync { } } - #[cfg(feature = "critical-section")] + #[cfg(not(feature = "std"))] #[test] #[should_panic(expected = "reentrant init")] fn reentrant_init() { @@ -658,7 +658,7 @@ mod sync { } } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn get_does_not_block() { let cell = OnceCell::new(); @@ -692,7 +692,7 @@ mod sync { #[cfg(feature = "race")] mod race { - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] use std::sync::Barrier; use std::{ num::NonZeroUsize, @@ -748,7 +748,7 @@ mod race { assert_eq!(cell.get(), Some(val1)); } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn once_non_zero_usize_first_wins() { let val1 = NonZeroUsize::new(92).unwrap(); @@ -828,14 +828,14 @@ mod race { #[cfg(all(feature = "race", feature = "alloc"))] mod race_once_box { - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] use std::sync::Barrier; use std::sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, }; - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] use crossbeam_utils::thread::scope; use once_cell::race::OnceBox; @@ -867,7 +867,7 @@ mod race_once_box { } } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn once_box_smoke_test() { let heap = Heap::default(); @@ -922,7 +922,7 @@ mod race_once_box { assert_eq!(heap.total(), 0); } - #[cfg(not(feature = "critical-section"))] + #[cfg(feature = "std")] #[test] fn once_box_first_wins() { let cell = OnceBox::new(); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index aaae075..2a5b66d 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -34,7 +34,7 @@ fn main() -> xshell::Result<()> { cmd!(sh, "cargo test --no-default-features --features unstable,alloc --test it").run()?; cmd!(sh, "cargo test --no-default-features --features critical-section").run()?; - cmd!(sh, "cargo test --no-default-features --features critical-section --release").run()?; + cmd!(sh, "cargo test --features critical-section").run()?; } {