use std::{mem, ptr};
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
struct Data<T>
{
lock : AtomicBool,
data : AtomicPtr<Arc<T>>,
_phantom : PhantomData<T>
}
impl<T> Data<T>
{
pub fn new(
data : Box<Arc<T>>
) -> Data<T>
{
Data {
lock : AtomicBool::new(false),
data : AtomicPtr::new(Box::into_raw(data)),
_phantom : PhantomData
}
}
fn lock<F, TT>(&self, callback : F) -> TT
where F : FnOnce() -> TT
{
loop {
if self.lock.compare_and_swap(false, true, Ordering::Relaxed) == false
{
break;
}
}
let result = callback();
self.lock.store(false, Ordering::Relaxed);
result
}
pub fn get(&self) -> Box<Arc<T>>
{
let (clone, src) = self.lock(|| {
unsafe {
let src = Box::from_raw(self.data.load(Ordering::Acquire));
(src.clone(), src)
}
});
mem::forget(src);
clone
}
pub fn set(&self, value : Box<Arc<T>>)
{
self.lock(|| {
unsafe {
Box::from_raw(self.data.swap(Box::into_raw(value), Ordering::AcqRel))
};
});
}
}
impl<T> Drop for Data<T>
{
fn drop(&mut self)
{
unsafe {
Box::from_raw(self.data.swap(ptr::null_mut(), Ordering::Acquire));
}
}
}
#[derive(Clone)]
pub struct SharedObject<T>
{
data : Arc<Data<T>>
}
impl<T> SharedObject<T>
{
pub fn new(
value : T
) -> SharedObject<T>
{
SharedObject {
data : Arc::new(Data::new(Box::new(Arc::new(value))))
}
}
pub fn set(
&self,
value : T
)
{
self.data.set(Box::new(Arc::new(value)));
}
pub fn get(&self) -> Box<Arc<T>>
{
self.data.get()
}
}
use std::fmt::{Debug, Display, Formatter, Error};
impl<T : Debug> Debug for SharedObject<T>
{
fn fmt(
&self,
f : &mut Formatter
) -> Result<(), Error>
{
write!(f, "{:?}", self.get())
}
}
impl<T : Display> Display for SharedObject<T>
{
fn fmt(
&self,
f : &mut Formatter
) -> Result<(), Error>
{
write!(f, "{}", self.get())
}
}
#[cfg(test)]
mod tests
{
#[test]
fn single()
{
let test = super::SharedObject::new(String::from("abc"));
assert_eq!(test.get().as_str(), "abc");
test.set(String::from("xyz"));
assert_eq!(test.get().as_str(), "xyz");
}
#[test]
fn multiple()
{
let test1 = super::SharedObject::new(String::from("abc"));
let test2 = test1.clone();
let test3 = test2.clone();
assert_eq!(test1.get().as_str(), "abc");
assert_eq!(test2.get().as_str(), "abc");
assert_eq!(test3.get().as_str(), "abc");
test1.set(String::from("xyz"));
assert_eq!(test1.get().as_str(), "xyz");
assert_eq!(test2.get().as_str(), "xyz");
assert_eq!(test3.get().as_str(), "xyz");
test2.set(String::from("mno"));
assert_eq!(test1.get().as_str(), "mno");
assert_eq!(test2.get().as_str(), "mno");
assert_eq!(test3.get().as_str(), "mno");
test3.set(String::from("123"));
assert_eq!(test1.get().as_str(), "123");
assert_eq!(test2.get().as_str(), "123");
assert_eq!(test3.get().as_str(), "123");
}
}