From ffb8aba311676a284d0490980045d67dbf8c5441 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Fri, 12 Jun 2020 13:15:21 -0700 Subject: [PATCH 1/2] Allow initialization using a const fn. --- src/lib.rs | 54 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 777475a..5829730 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,34 +139,16 @@ macro_rules! static_atomic_ref { static_atomic_ref!(@PUB, $(#[$attr])* static $N : $T; $($t)*); }; (@$VIS:ident, $(#[$attr:meta])* static $N:ident : $T:ty; $($t:tt)*) => { - static_atomic_ref!(@MAKE TY, $VIS, $(#[$attr])*, $N); - impl $crate::core_ops::Deref for $N { - type Target = $crate::AtomicRef<'static, $T>; - #[allow(unsafe_code)] - fn deref<'a>(&'a self) -> &'a $crate::AtomicRef<'static, $T> { - static STORAGE: $crate::AtomicRef<'static, u8> = $crate::ATOMIC_U8_REF_INIT; - unsafe { $crate::core_mem::transmute(&STORAGE) } - } - } + static_atomic_ref!(@MAKE TY, $VIS, $(#[$attr])*, $N, $T); static_atomic_ref!($($t)*); }; - (@MAKE TY, PUB, $(#[$attr:meta])*, $N:ident) => { - #[allow(missing_copy_implementations)] - #[allow(non_camel_case_types)] - #[allow(dead_code)] + (@MAKE TY, PUB, $(#[$attr:meta])*, $N:ident, $T:ty) => { $(#[$attr])* - pub struct $N { _private: () } - #[doc(hidden)] - pub static $N: $N = $N { _private: () }; + pub static $N: $crate::AtomicRef<'static, $T> = $crate::AtomicRef::static_none(); }; - (@MAKE TY, PRIV, $(#[$attr:meta])*, $N:ident) => { - #[allow(missing_copy_implementations)] - #[allow(non_camel_case_types)] - #[allow(dead_code)] + (@MAKE TY, PRIV, $(#[$attr:meta])*, $N:ident, $T:ty) => { $(#[$attr])* - struct $N { _private: () } - #[doc(hidden)] - static $N: $N = $N { _private: () }; + static $N: $crate::AtomicRef<'static, $T> = $crate::AtomicRef::static_none(); }; () => (); } @@ -186,6 +168,32 @@ unsafe fn to_opt<'a, T>(p: usize) -> Option<&'a T> { (p as *const T).as_ref() } +impl AtomicRef<'static, T> { + // Putting this inside `static_none` hits a "no mutable references in const + // fn" limitation, because of the `PhantomData`. Other methods of enforcing + // invariance hit the same sort of problem (`fn` isn't allowed either). + const NONE: Self = Self { + data: AtomicUsize::new(0), + _marker: PhantomData, + }; + /// Returns a `AtomicRef<'static, T>` with a value of `None`. + /// + /// This is useful as it is implemented as a `const fn`, and thus can + /// initialize an `AtomicRef` used as a `static`. + /// + /// # Examples + /// + /// ``` + /// use atomic_ref::AtomicRef; + /// + /// pub static SOME_REFERENCE: AtomicRef<'static, u64> = AtomicRef::static_none(); + /// ``` + #[inline] + pub const fn static_none() -> Self { + Self::NONE + } +} + impl<'a, T> AtomicRef<'a, T> { /// Creates a new `AtomicRef`. /// From f1366a49bb208930144d5dd0b1119c967385302c Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Fri, 12 Jun 2020 13:38:31 -0700 Subject: [PATCH 2/2] Add a `AtomicRef::static_some` function allowing providing a non-empty initial value --- src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5829730..942a5a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,7 +73,8 @@ //! ``` #![no_std] -use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use core::sync::atomic::{AtomicPtr, Ordering}; +use core::ptr::null_mut; use core::marker::PhantomData; use core::fmt; use core::default::Default; @@ -81,7 +82,7 @@ use core::default::Default; /// A mutable Option<&'a, T> type which can be safely shared between threads. #[repr(C)] pub struct AtomicRef<'a, T: 'a> { - data: AtomicUsize, + data: AtomicPtr, // Make `AtomicRef` invariant over `'a` and `T` _marker: PhantomData<&'a mut &'a mut T>, } @@ -101,7 +102,7 @@ pub struct AtomicRef<'a, T: 'a> { /// Please use `static_atomic_ref!` instead of this constant if you need to /// implement a static atomic reference variable. pub const ATOMIC_U8_REF_INIT: AtomicRef<'static, u8> = AtomicRef { - data: ATOMIC_USIZE_INIT, + data: AtomicPtr::new(null_mut()), _marker: PhantomData, }; @@ -153,18 +154,18 @@ macro_rules! static_atomic_ref { () => (); } -/// An internal helper function for converting `Option<&'a T>` values to usize -/// for storing in the `AtomicUsize`. -fn from_opt<'a, T>(p: Option<&'a T>) -> usize { +/// An internal helper function for converting `Option<&'a T>` values to `*mut T` +/// for storing in the `AtomicPtr`. +fn from_opt<'a, T>(p: Option<&'a T>) -> *mut T { match p { - Some(p) => p as *const T as usize, - None => 0, + Some(p) => p as *const T as *mut T, + None => null_mut(), } } -/// An internal helper function for converting `usize` values stored in the -/// `AtomicUsize` back into `Option<&'a T>` values. -unsafe fn to_opt<'a, T>(p: usize) -> Option<&'a T> { +/// An internal helper function for converting `*mut T` values stored in the +/// `AtomicPtr` back into `Option<&'a T>` values. +unsafe fn to_opt<'a, T>(p: *mut T) -> Option<&'a T> { (p as *const T).as_ref() } @@ -173,7 +174,7 @@ impl AtomicRef<'static, T> { // fn" limitation, because of the `PhantomData`. Other methods of enforcing // invariance hit the same sort of problem (`fn` isn't allowed either). const NONE: Self = Self { - data: AtomicUsize::new(0), + data: AtomicPtr::new(null_mut()), _marker: PhantomData, }; /// Returns a `AtomicRef<'static, T>` with a value of `None`. @@ -192,6 +193,31 @@ impl AtomicRef<'static, T> { pub const fn static_none() -> Self { Self::NONE } + + /// Returns a `AtomicRef<'static, T>` with a value of `Some(arg)`. + /// + /// This is useful as it is implemented as a `const fn`, and thus can + /// initialize an `AtomicRef` used as a `static`. + /// + /// # Examples + /// + /// ``` + /// use atomic_ref::AtomicRef; + /// use std::sync::atomic::Ordering; + /// + /// static INITIAL: u64 = 123; + /// + /// pub static SOME_REFERENCE: AtomicRef<'static, u64> = AtomicRef::static_some(&INITIAL); + /// + /// assert_eq!(Some(&123), SOME_REFERENCE.load(Ordering::SeqCst)); + /// ``` + #[inline] + pub const fn static_some(init: &'static T) -> Self { + Self { + data: AtomicPtr::new(init as *const T as *mut T), + ..Self::NONE + } + } } impl<'a, T> AtomicRef<'a, T> { @@ -207,7 +233,7 @@ impl<'a, T> AtomicRef<'a, T> { /// ``` pub fn new(p: Option<&'a T>) -> AtomicRef<'a, T> { AtomicRef { - data: AtomicUsize::new(from_opt(p)), + data: AtomicPtr::new(from_opt(p)), _marker: PhantomData, } } @@ -432,4 +458,14 @@ mod tests { assert!(FOO.load(Ordering::SeqCst) == Some(&A)); assert!(FOO.load(Ordering::SeqCst).unwrap() as *const _ == &A as *const _); } + + static BAR: super::AtomicRef<'static, i32> = super::AtomicRef::static_some(&A); + + #[test] + fn static_some() { + assert_eq!(BAR.load(Ordering::SeqCst), Some(&10)); + assert_eq!(BAR.load(Ordering::SeqCst).unwrap() as *const _, &A as *const _); + BAR.store(None, Ordering::SeqCst); + assert_eq!(BAR.load(Ordering::SeqCst), None); + } }