From 00420d6807a87edcc6bc30e913ce89e295da1d81 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 26 Aug 2023 00:01:14 +0200 Subject: [PATCH] Implement wrapper for OnceLock --- CHANGELOG.md | 1 + src/stdsync.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 145 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db6e3d..d88cbc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - The minimum supported Rust version is now defined as 1.70. Previously it was undefined. - Wrappers for `std::sync` primitives can now be `const` constructed. +- Add support for `std::sync::OnceLock` ### Breaking diff --git a/src/stdsync.rs b/src/stdsync.rs index 149caad..b9b237d 100644 --- a/src/stdsync.rs +++ b/src/stdsync.rs @@ -21,10 +21,14 @@ pub use std::sync as raw; #[cfg(not(debug_assertions))] -pub use std::sync::{Condvar, Mutex, MutexGuard, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub use std::sync::{ + Condvar, Mutex, MutexGuard, Once, OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard, +}; #[cfg(debug_assertions)] -pub use tracing::{Condvar, Mutex, MutexGuard, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; +pub use tracing::{ + Condvar, Mutex, MutexGuard, Once, OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard, +}; /// Dependency tracing versions of [`std::sync`]. pub mod tracing { @@ -427,8 +431,8 @@ pub mod tracing { /// Wrapper around [`std::sync::Once`]. /// - /// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct and - /// the one it wraps. + /// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct + /// and the one it wraps. #[derive(Debug)] pub struct Once { inner: sync::Once, @@ -479,6 +483,142 @@ pub mod tracing { } } + /// Wrapper for [`std::sync::OnceLock`] + /// + /// The exact locking behaviour of [`std::sync::OnceLock`] is currently undefined, but may + /// deadlock in the event of reentrant initialization attempts. This wrapper participates in + /// cycle detection as normal and will therefore panic in the event of reentrancy. + /// + /// Most of this primitive's methods do not involve locking and as such are simply passed + /// through to the inner implementation. + /// + /// # Examples + /// + /// ``` + /// use tracing_mutex::stdsync::tracing::OnceLock; + /// + /// static LOCK: OnceLock = OnceLock::new(); + /// assert!(LOCK.get().is_none()); + /// + /// std::thread::spawn(|| { + /// let value: &i32 = LOCK.get_or_init(|| 42); + /// assert_eq!(value, &42); + /// }).join().unwrap(); + /// + /// let value: Option<&i32> = LOCK.get(); + /// assert_eq!(value, Some(&42)); + /// ``` + #[derive(Debug)] + pub struct OnceLock { + id: LazyMutexId, + inner: sync::OnceLock, + } + + // N.B. this impl inlines everything that directly calls the inner implementation as there + // should be 0 overhead to doing so. + impl OnceLock { + /// Creates a new empty cell + pub const fn new() -> Self { + Self { + id: LazyMutexId::new(), + inner: sync::OnceLock::new(), + } + } + + /// Gets a reference to the underlying value. + /// + /// This method does not attempt to lock and therefore does not participate in cycle + /// detection. + #[inline] + pub fn get(&self) -> Option<&T> { + self.inner.get() + } + + /// Gets a mutable reference to the underlying value. + /// + /// This method does not attempt to lock and therefore does not participate in cycle + /// detection. + #[inline] + pub fn get_mut(&mut self) -> Option<&mut T> { + self.inner.get_mut() + } + + /// Sets the contents of this cell to the underlying value + /// + /// As this method may block until initialization is complete, it participates in cycle + /// detection. + pub fn set(&self, value: T) -> Result<(), T> { + let _guard = self.id.get_borrowed(); + + self.inner.set(value) + } + + /// Gets the contents of the cell, initializing it with `f` if the cell was empty. + /// + /// This method participates in cycle detection. Reentrancy is considered a cycle. + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + let _guard = self.id.get_borrowed(); + self.inner.get_or_init(f) + } + + /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. + /// + /// This method does not attempt to lock and therefore does not participate in cycle + /// detection. + #[inline] + pub fn take(&mut self) -> Option { + self.inner.take() + } + + /// Consumes the `OnceLock`, returning the wrapped value. Returns None if the cell was + /// empty. + /// + /// This method does not attempt to lock and therefore does not participate in cycle + /// detection. + #[inline] + pub fn into_inner(mut self) -> Option { + self.take() + } + } + + impl Default for OnceLock { + #[inline] + fn default() -> Self { + Self::new() + } + } + + impl PartialEq for OnceLock { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } + } + + impl Eq for OnceLock {} + + impl Clone for OnceLock { + fn clone(&self) -> Self { + Self { + id: LazyMutexId::new(), + inner: self.inner.clone(), + } + } + } + + impl From for OnceLock { + #[inline] + fn from(value: T) -> Self { + Self { + id: LazyMutexId::new(), + inner: sync::OnceLock::from(value), + } + } + } + #[cfg(test)] mod tests { use std::sync::Arc;