You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
2.5 KiB
120 lines
2.5 KiB
#![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());
|
|
}
|
|
|