From 50141d382e5ca1fb10f5e0a56de44b7301d6f3ad Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 4 Aug 2018 11:40:01 +0300 Subject: [PATCH] Don't allocate in parking lot impl --- src/imp_pl.rs | 126 ++++++++++++++++++++++++++++++++++++++++ src/imp_std.rs | 129 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 155 +++---------------------------------------------- src/sync.rs | 0 4 files changed, 263 insertions(+), 147 deletions(-) create mode 100644 src/imp_pl.rs create mode 100644 src/imp_std.rs create mode 100644 src/sync.rs diff --git a/src/imp_pl.rs b/src/imp_pl.rs new file mode 100644 index 0000000..ee22fab --- /dev/null +++ b/src/imp_pl.rs @@ -0,0 +1,126 @@ +extern crate parking_lot; + +use std::cell::UnsafeCell; +use self::parking_lot::{Once, ONCE_INIT, OnceState}; + +/// A thread-safe cell which can be written to only once. +/// +/// Unlike `::std::sync::Mutex`, a `OnceCell` provides simple `&` +/// references to the contents. +/// +/// # Example +/// ``` +/// use once_cell::sync::OnceCell; +/// +/// static CELL: OnceCell = OnceCell::INIT; +/// assert!(CELL.get().is_none()); +/// +/// ::std::thread::spawn(|| { +/// let value: &String = CELL.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// }).join().unwrap(); +/// +/// let value: Option<&String> = CELL.get(); +/// assert!(value.is_some()); +/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); +/// ``` +#[derive(Debug)] +pub struct OnceCell { + once: Once, + value: UnsafeCell>, +} + +impl OnceCell { + /// An empty cell, for initialization in a `const` context. + pub const INIT: OnceCell = OnceCell { + once: ONCE_INIT, + value: UnsafeCell::new(None), + }; + + /// Creates a new empty cell. + pub fn new() -> OnceCell { + OnceCell { + once: ONCE_INIT, + value: UnsafeCell::new(None), + } + } + + /// Gets the reference to the underlying value. Returns `None` + /// if the cell is empty. + pub fn get(&self) -> Option<&T> { + if self.once.state() == OnceState::Done { + let value: &Option = unsafe { &*self.value.get() }; + value.as_ref() + } else { + None + } + } + + /// Sets the contents of this cell to `value`. Returns + /// `Ok(())` if the cell was empty and `Err(value)` if it was + /// full. + /// + /// # Example + /// ``` + /// use once_cell::sync::OnceCell; + /// + /// static CELL: OnceCell = OnceCell::INIT; + /// + /// fn main() { + /// assert!(CELL.get().is_none()); + /// + /// ::std::thread::spawn(|| { + /// assert_eq!(CELL.set(92), Ok(())); + /// }).join().unwrap(); + /// + /// assert_eq!(CELL.set(62), Err(62)); + /// assert_eq!(CELL.get(), Some(&92)); + /// } + /// ``` + pub fn set(&self, value: T) -> Result<(), T> { + let mut value = Some(value); + self.once.call_once(|| { + let slot: &mut Option = unsafe { &mut *self.value.get() }; + *slot = value.take(); + }); + match value { + None => Ok(()), + Some(value) => Err(value) + } + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. May threads may call `get_or_init` + /// concurrently with different initializing functions, but + /// it is guaranteed that only one function will be executed. + /// + /// # Example + /// ``` + /// use once_cell::sync::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + pub fn get_or_init T>(&self, f: F) -> &T { + self.once.call_once(|| { + let value = f(); + let slot: &mut Option = unsafe { &mut *self.value.get() }; + *slot = Some(value); + }); + self.get().unwrap() + } +} + +// Why do we need `T: Send`? +// Thread A creates a `OnceCell` and shares it with +// scoped thread B, which fills the cell, which is +// then destroyed by A. That is, destructor observes +// a sent value. +unsafe impl Sync for OnceCell {} + +unsafe impl Send for OnceCell {} diff --git a/src/imp_std.rs b/src/imp_std.rs new file mode 100644 index 0000000..e1db1e7 --- /dev/null +++ b/src/imp_std.rs @@ -0,0 +1,129 @@ +use std::{ + ptr, + sync::{ + Once, ONCE_INIT, + atomic::{AtomicPtr, Ordering}, + }, +}; + +/// A thread-safe cell which can be written to only once. +/// +/// Unlike `::std::sync::Mutex`, a `OnceCell` provides simple `&` +/// references to the contents. +/// +/// # Example +/// ``` +/// use once_cell::sync::OnceCell; +/// +/// static CELL: OnceCell = OnceCell::INIT; +/// assert!(CELL.get().is_none()); +/// +/// ::std::thread::spawn(|| { +/// let value: &String = CELL.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// }).join().unwrap(); +/// +/// let value: Option<&String> = CELL.get(); +/// assert!(value.is_some()); +/// assert_eq!(value.unwrap().as_str(), "Hello, World!"); +/// ``` +#[derive(Debug)] +pub struct OnceCell { + value: AtomicPtr, + once: Once, +} + +impl OnceCell { + /// An empty cell, for initialization in a `const` context. + pub const INIT: OnceCell = OnceCell { + value: AtomicPtr::new(ptr::null_mut()), + once: ONCE_INIT, + }; + + /// Creates a new empty cell. + pub fn new() -> OnceCell { + OnceCell { + value: AtomicPtr::new(ptr::null_mut()), + once: Once::new(), + } + } + + /// Gets the reference to the underlying value. Returns `None` + /// if the cell is empty. + pub fn get(&self) -> Option<&T> { + let ptr = self.value.load(Ordering::Acquire); + unsafe { ptr.as_ref() } + } + + /// Sets the contents of this cell to `value`. Returns + /// `Ok(())` if the cell was empty and `Err(value)` if it was + /// full. + /// + /// # Example + /// ``` + /// use once_cell::sync::OnceCell; + /// + /// static CELL: OnceCell = OnceCell::INIT; + /// + /// fn main() { + /// assert!(CELL.get().is_none()); + /// + /// ::std::thread::spawn(|| { + /// assert_eq!(CELL.set(92), Ok(())); + /// }).join().unwrap(); + /// + /// assert_eq!(CELL.set(62), Err(62)); + /// assert_eq!(CELL.get(), Some(&92)); + /// } + /// ``` + pub fn set(&self, value: T) -> Result<(), T> { + let mut value = Some(value); + self.once.call_once(|| { + let value = value.take().unwrap(); + unsafe { self.set_inner(value) } + }); + match value { + None => Ok(()), + Some(value) => Err(value) + } + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. May threads may call `get_or_init` + /// concurrently with different initializing functions, but + /// it is guaranteed that only one function will be executed. + /// + /// # Example + /// ``` + /// use once_cell::sync::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + pub fn get_or_init T>(&self, f: F) -> &T { + self.once.call_once(|| { + let value = f(); + unsafe { self.set_inner(value); } + }); + self.get().unwrap() + } + + unsafe fn set_inner(&self, value: T) { + let ptr = Box::into_raw(Box::new(value)); + self.value.store(ptr, Ordering::Release); + } +} + +impl Drop for OnceCell { + fn drop(&mut self) { + let ptr = self.value.load(Ordering::Acquire); + if !ptr.is_null() { + drop(unsafe { Box::from_raw(ptr) }) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5c42a71..19c738e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,9 +166,12 @@ of `1.29.0` is required. This crate uses unsafe. */ - #[cfg(feature = "parking_lot")] -extern crate parking_lot; +#[path="imp_pl.rs"] +mod imp; +#[cfg(not(feature = "parking_lot"))] +#[path="imp_std.rs"] +mod imp; #[macro_use] pub mod unsync { @@ -387,151 +390,9 @@ pub mod unsync { #[macro_use] pub mod sync { - use std::{ - ptr, - sync::{atomic::{AtomicPtr, Ordering}}, - }; - #[cfg(feature = "parking_lot")] - use parking_lot::{Once, ONCE_INIT}; - #[cfg(not(feature = "parking_lot"))] - use std::sync::{Once, ONCE_INIT}; - - /// A thread-safe cell which can be written to only once. - /// - /// Unlike `::std::sync::Mutex`, a `OnceCell` provides simple `&` - /// references to the contents. - /// - /// # Example - /// ``` - /// use once_cell::sync::OnceCell; - /// - /// static CELL: OnceCell = OnceCell::INIT; - /// assert!(CELL.get().is_none()); - /// - /// ::std::thread::spawn(|| { - /// let value: &String = CELL.get_or_init(|| { - /// "Hello, World!".to_string() - /// }); - /// assert_eq!(value, "Hello, World!"); - /// }).join().unwrap(); - /// - /// let value: Option<&String> = CELL.get(); - /// assert!(value.is_some()); - /// assert_eq!(value.unwrap().as_str(), "Hello, World!"); - /// ``` - #[derive(Debug)] - pub struct OnceCell { - // Invariant 1: `inner` is written to only from within `once.call_once`. - // Corollary 1: inner is written at most once. - // Invariant 2: if not null, ptr came from `Box::into_raw`. - inner: AtomicPtr, - once: Once, - } - - // Why do we need `T: Send`? - // Thread A creates a `OnceCell` and shares it with - // scoped thread B, which fills the cell, which is - // then destroyed by A. That is, destructor observes - // a sent value. - unsafe impl Sync for OnceCell {} - - unsafe impl Send for OnceCell {} - - impl OnceCell { - /// An empty cell, for initialization in a `const` context. - pub const INIT: OnceCell = OnceCell { - inner: AtomicPtr::new(ptr::null_mut()), - once: ONCE_INIT, - }; - - /// Creates a new empty cell. - pub fn new() -> OnceCell { - OnceCell { - inner: AtomicPtr::new(ptr::null_mut()), - once: Once::new(), - } - } - - /// Gets the reference to the underlying value. Returns `None` - /// if the cell is empty. - pub fn get(&self) -> Option<&T> { - let ptr = self.inner.load(Ordering::Acquire); - // Safe due to Corollary 1 - unsafe { ptr.as_ref() } - } - - /// Sets the contents of this cell to `value`. Returns - /// `Ok(())` if the cell was empty and `Err(value)` if it was - /// full. - /// - /// # Example - /// ``` - /// use once_cell::sync::OnceCell; - /// - /// static CELL: OnceCell = OnceCell::INIT; - /// - /// fn main() { - /// assert!(CELL.get().is_none()); - /// - /// ::std::thread::spawn(|| { - /// assert_eq!(CELL.set(92), Ok(())); - /// }).join().unwrap(); - /// - /// assert_eq!(CELL.set(62), Err(62)); - /// assert_eq!(CELL.get(), Some(&92)); - /// } - /// ``` - pub fn set(&self, value: T) -> Result<(), T> { - let mut value = Some(value); - self.once.call_once(|| { - let value = value.take().unwrap(); - unsafe { self.set_inner(value) } - }); - match value { - None => Ok(()), - Some(value) => Err(value) - } - } - - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. May threads may call `get_or_init` - /// concurrently with different initializing functions, but - /// it is guaranteed that only one function will be executed. - /// - /// # Example - /// ``` - /// use once_cell::sync::OnceCell; - /// - /// let cell = OnceCell::new(); - /// let value = cell.get_or_init(|| 92); - /// assert_eq!(value, &92); - /// let value = cell.get_or_init(|| unreachable!()); - /// assert_eq!(value, &92); - /// ``` - pub fn get_or_init T>(&self, f: F) -> &T { - self.once.call_once(|| { - let value = f(); - unsafe { self.set_inner(value); } - }); - self.get().unwrap() - } - - // Invariant: must be called from `self.once`. - unsafe fn set_inner(&self, value: T) { - let ptr = Box::into_raw(Box::new(value)); - self.inner.store(ptr, Ordering::Release); - } - } - - impl Drop for OnceCell { - fn drop(&mut self) { - let ptr = self.inner.load(Ordering::Acquire); - if !ptr.is_null() { - // Safe due to Corollary 2 - drop(unsafe { Box::from_raw(ptr) }) - } - } - } + // Can't use `OnceCell(imp::OnceCell) due to + // https://github.com/rust-lang/rust/issues/50518 + pub use imp::OnceCell; /// A value which is initialized on the first access. /// diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..e69de29