#![feature(external_doc)] #![doc(include = "../README.md")] use std::sync::{ atomic::{AtomicPtr, Ordering}, Arc, }; use std::{cmp::PartialEq, ops::Deref}; /// An alias for a referenced pointer pub struct Ref { inner: Box>, } impl Deref for Ref { type Target = Arc; fn deref(&self) -> &Self::Target { &self.inner } } /// A safe atomic pointer wrapper #[derive(Clone, Debug)] pub struct AtomPtr { inner: Arc>>, } // Implement Default for all T that implement default impl Default for AtomPtr { fn default() -> Self { Self::new(T::default()) } } impl PartialEq for AtomPtr { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.get_ref().inner, &other.get_ref().inner) } } impl AtomPtr { fn make_raw_ptr(t: T) -> *mut Arc { Box::into_raw(Box::new(Arc::new(t))) } /// Create a new atomic pointer for a type pub fn new(t: T) -> Self { let ptr = Self::make_raw_ptr(t); let inner = Arc::new(AtomicPtr::from(ptr)); Self { inner } } /// Get an immutable reference to the current value pub fn get_ref(&self) -> Ref { let ptr = self.inner.load(Ordering::Relaxed); let b = unsafe { Box::from_raw(ptr) }; let arc = Arc::clone(&*b); std::mem::forget(b); Ref { inner: Box::new(arc), } } /// Swap the data entry with a new value, returning the old pub fn swap(&self, new: T) -> Ref { let new = Self::make_raw_ptr(new); let prev = self.inner.swap(new, Ordering::Relaxed); let b = unsafe { Box::from_raw(prev) }; let arc = Arc::clone(&*b); std::mem::forget(b); Ref { inner: Box::new(arc), } } } #[cfg(test)] #[derive(Clone, Debug, PartialEq)] struct TestStruct { name: String, } #[test] fn cloned() { let ts = TestStruct { name: "Hello".into(), }; let ptr1 = AtomPtr::new(ts); let ptr2 = ptr1.clone(); assert_eq!(ptr1, ptr2); } #[test] fn swap() { let ts1 = TestStruct { name: "Hello 1".into(), }; let ts2 = TestStruct { name: "Hello 2".into(), }; // Make an AtomPtr with some data let ptr = AtomPtr::new(ts1.clone()); assert_eq!(ptr.get_ref().name, "Hello 1".to_string()); // Swap the data let still_ts1 = ptr.swap(ts2); assert_eq!(ptr.get_ref().name, "Hello 2".to_string()); // But the old ref is still valid assert_eq!(ts1, *still_ts1.as_ref()); }