From 8a97a58e51edfcf563882a7ec1f48532bc0fd2f9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 1 Jan 2020 23:39:56 +0100 Subject: [PATCH] Add no-threads mode, for use in single-threaded environments --- Cargo.toml | 4 ++++ src/imp_std.rs | 24 ++++++++++++++++++++---- src/lib.rs | 10 ++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index caaec03..8bbca17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,10 @@ regex = "1.2.0" default = ["std"] # Enables `once_cell::sync` module. std = [] +# Enables `sync` module for `no_std`, assuming that no concurrent initialization of `OnceCell` can happen. +# This is mostly intended for WASM. +# If concurrent initialization does happen, the result is a panic. +assume-no-threads-or-interrupts = [] [[example]] name = "bench" diff --git a/src/imp_std.rs b/src/imp_std.rs index f5a6ea5..a2da897 100644 --- a/src/imp_std.rs +++ b/src/imp_std.rs @@ -3,11 +3,17 @@ // * no poisoning // * init function can fail +use core::{ + cell::UnsafeCell, + sync::atomic::{AtomicUsize, Ordering}, +}; + +#[cfg(feature = "std")] use std::{ - cell::{Cell, UnsafeCell}, + cell::Cell, marker::PhantomData, panic::{RefUnwindSafe, UnwindSafe}, - sync::atomic::{AtomicBool, AtomicUsize, Ordering}, + sync::atomic::AtomicBool, thread::{self, Thread}, }; @@ -16,12 +22,13 @@ pub(crate) struct OnceCell { // This `state` word is actually an encoded version of just a pointer to a // `Waiter`, so we add the `PhantomData` appropriately. state_and_queue: AtomicUsize, - _marker: PhantomData<*mut Waiter>, // FIXME: switch to `std::mem::MaybeUninit` once we are ready to bump MSRV // 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>, + #[cfg(feature = "std")] + _marker: PhantomData<*mut Waiter>, } // Why do we need `T: Send`? @@ -32,7 +39,9 @@ pub(crate) struct OnceCell { unsafe impl Sync for OnceCell {} unsafe impl Send for OnceCell {} +#[cfg(feature = "std")] impl RefUnwindSafe for OnceCell {} +#[cfg(feature = "std")] impl UnwindSafe for OnceCell {} // Three states that a OnceCell can be in, encoded into the lower bits of `state` in @@ -46,6 +55,7 @@ const COMPLETE: usize = 0x2; const STATE_MASK: usize = 0x3; // Representation of a node in the linked list of waiters in the RUNNING state. +#[cfg(feature = "std")] #[repr(align(4))] // Ensure the two lower bits are free to use as state bits. struct Waiter { thread: Cell>, @@ -65,8 +75,9 @@ impl OnceCell { pub(crate) const fn new() -> OnceCell { OnceCell { state_and_queue: AtomicUsize::new(INCOMPLETE), - _marker: PhantomData, value: UnsafeCell::new(None), + #[cfg(feature = "std")] + _marker: PhantomData, } } @@ -136,6 +147,9 @@ fn initialize_inner(my_state_and_queue: &AtomicUsize, init: &mut dyn FnMut() -> waiter_queue.set_state_on_drop_to = if success { COMPLETE } else { INCOMPLETE }; return success; } + #[cfg(not(feature = "std"))] + _ => panic!("assume-no-threads-or-interrupts is set, but there are threads!"), + #[cfg(feature = "std")] _ => { assert!(state_and_queue & STATE_MASK == RUNNING); wait(&my_state_and_queue, state_and_queue); @@ -146,6 +160,7 @@ fn initialize_inner(my_state_and_queue: &AtomicUsize, init: &mut dyn FnMut() -> } // Copy-pasted from std exactly. +#[cfg(feature = "std")] fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { loop { if current_state & STATE_MASK != RUNNING { @@ -180,6 +195,7 @@ impl Drop for WaiterQueue<'_> { assert_eq!(state_and_queue & STATE_MASK, RUNNING); + #[cfg(feature = "std")] unsafe { let mut queue = (state_and_queue & !STATE_MASK) as *const Waiter; while !queue.is_null() { diff --git a/src/lib.rs b/src/lib.rs index 95138ce..35e4182 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ might be easier to debug than a deadlock. #[path = "imp_pl.rs"] mod imp; -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "assume-no-threads-or-interrupts"))] #[cfg(not(feature = "parking_lot"))] #[path = "imp_std.rs"] mod imp; @@ -558,9 +558,11 @@ pub mod unsync { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "assume-no-threads-or-interrupts"))] pub mod sync { - use std::{cell::Cell, fmt, hint::unreachable_unchecked, panic::RefUnwindSafe}; + use core::{cell::Cell, fmt, hint::unreachable_unchecked, ops::Deref}; + #[cfg(feature = "std")] + use std::panic::RefUnwindSafe; use crate::imp::OnceCell as Imp; @@ -895,7 +897,7 @@ pub mod sync { } } - impl T> ::std::ops::Deref for Lazy { + impl T> Deref for Lazy { type Target = T; fn deref(&self) -> &T { Lazy::force(self)