Skip to content

Allow initialization using a const fn, and initialization to values other than None #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 79 additions & 35 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,16 @@
//! ```
#![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;

/// 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<T>,
// Make `AtomicRef` invariant over `'a` and `T`
_marker: PhantomData<&'a mut &'a mut T>,
}
Expand All @@ -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,
};

Expand Down Expand Up @@ -139,53 +140,86 @@ 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();
};
() => ();
}

/// 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()
}

impl<T: 'static> 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: AtomicPtr::new(null_mut()),
_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
}

/// 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> {
/// Creates a new `AtomicRef`.
///
Expand All @@ -199,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,
}
}
Expand Down Expand Up @@ -424,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);
}
}