Skip to content
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);
}
}