diff options
Diffstat (limited to 'development/libs/atomptr/src/lib.rs')
-rw-r--r-- | development/libs/atomptr/src/lib.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/development/libs/atomptr/src/lib.rs b/development/libs/atomptr/src/lib.rs new file mode 100644 index 000000000000..92410fc4bc38 --- /dev/null +++ b/development/libs/atomptr/src/lib.rs @@ -0,0 +1,120 @@ +#![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<T> { + inner: Box<Arc<T>>, +} + +impl<T> Deref for Ref<T> { + type Target = Arc<T>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +/// A safe atomic pointer wrapper +#[derive(Clone, Debug)] +pub struct AtomPtr<T> { + inner: Arc<AtomicPtr<Arc<T>>>, +} + +// Implement Default for all T that implement default +impl<T: Default> Default for AtomPtr<T> { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl<T> PartialEq for AtomPtr<T> { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.get_ref().inner, &other.get_ref().inner) + } +} + +impl<T> AtomPtr<T> { + fn make_raw_ptr(t: T) -> *mut Arc<T> { + 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<T> { + 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<T> { + 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()); +} |