1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! A safe, strongly typed (generic) atomic pointer abstraction to build
//! datastructures, and lock-free algorithms on top of.  Only uses
//! `libstd`.
//! 
//! The standard library contains an `AtomicPtr` type, which by itself
//! isn't very ergonomic to use, because it deals with raw pointers.  This
//! library assumes that types can always be heap allocated, wrapping them
//! in a `Box<T>`, and provides a nicer (and safe!) abstraction for
//! `std::sync::atomic::AtomicPtr`.  Using this crate is fairely
//! self-explanatory:
//! 
//! ```rust
//! use atomptr::AtomPtr;
//! 
//! struct MyData { name: String }
//! let data = MyData { name: "Kookie".into() };
//! 
//! let a = AtomPtr::new(data);
//! println!("Name is: {}", a.get_ref().name);
//! 
//! let old_ref = a.swap(MyData { name: "Bob".into() });
//! println!("Name now is: {}, was {}", a.get_ref().name, old_ref.name);
//! ```
//! 
//! Note that the type that is returned by `get_ref` and `swap` is
//! `Ref<T>`, which means that the old data is not de-allocated after a
//! swap, before this last reference goes out of scope.  You can of course
//! always manually call `drop()` on it.



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