shareable 0.1.1

Thread shareable objects using the minimal amount of synchronization.
Documentation
/* Copyright 2016 Joshua Gentry
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */
use std::{mem, ptr};
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};

//*************************************************************************************************
/// Internal data structure that controls the life of the atomic pointer and contains the spin
/// lock for accessing the pointer.
struct Data<T>
{
    //---------------------------------------------------------------------------------------------
    /// A spin lock so that the value pointed to by data is not deleted before the reference count
    /// can be incremented.
    lock : AtomicBool,

    //---------------------------------------------------------------------------------------------
    /// The atomic pointer to the object we're encapsulating.
    data : AtomicPtr<Arc<T>>,

    //---------------------------------------------------------------------------------------------
    /// A phantom data object so the compiler behaves correctly.
    _phantom : PhantomData<T>
}

impl<T> Data<T>
{
    //*********************************************************************************************
    /// Create a new instance of the object.
    pub fn new(
        data : Box<Arc<T>>
        ) -> Data<T>
    {
        Data {
            lock : AtomicBool::new(false),
            data : AtomicPtr::new(Box::into_raw(data)),
            _phantom : PhantomData
        }
    }

    //*********************************************************************************************
    /// This method is used to control access to the atomic pointer.
    fn lock<F, TT>(&self, callback : F) -> TT
        where F : FnOnce() -> TT
    {
        //-----------------------------------------------------------------------------------------
        // Spin to get the lock.
        loop {
            if  self.lock.compare_and_swap(false, true, Ordering::Relaxed) == false
            {
                break;
            }
        }

        //-----------------------------------------------------------------------------------------
        // Call the method.
        let result = callback();

        //-----------------------------------------------------------------------------------------
        // Release the lock and return.
        self.lock.store(false, Ordering::Relaxed);

        result
    }

    //*********************************************************************************************
    /// Returns the object.  First the spin lock is acquired and the object pointer is extracted
    /// and converted to a box.  The reference count is then incremented with the clone() call.
    ///
    /// Outside of the lock the original instance is forgotten so rust doesn't free it and the
    /// clone is returned.
    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
    }

    //*********************************************************************************************
    /// Stores an object in the atomic pointer.  This method will acquire the spin lock then swap
    /// the new value in.
    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>
{
    //*********************************************************************************************
    /// When we drop, we need to grab the object in the atomic pointer and free it.
    fn drop(&mut self)
    {
        unsafe {
            Box::from_raw(self.data.swap(ptr::null_mut(), Ordering::Acquire));
        }
    }
}

//*************************************************************************************************
/// Shareable object data element.
///
/// Shares a read only object between multiple instances using an AtomicPtr and a small sping
/// lock.
///
/// # Examples
///
/// ```
/// use std::sync::mpsc;
/// use std::thread;
/// use shareable::SharedObject;
///
/// let value1 = SharedObject::new(String::from("abc"));
/// let value2 = value1.clone();
///
/// let (tx, rx) = mpsc::channel();
///
/// let thread = thread::spawn(move || {
///     rx.recv();
///     assert_eq!(value2.get().as_str(), "xyz");
/// });
///
/// value1.set(String::from("xyz"));
///
/// tx.send(());
/// thread.join().unwrap();
/// ```
#[derive(Clone)]
pub struct SharedObject<T>
{
    //---------------------------------------------------------------------------------------------
    /// The internal data element.
    data : Arc<Data<T>>
}

impl<T> SharedObject<T>
{
    //********************************************************************************************
    /// Construct a new instance of the object.
    pub fn new(
        value : T
        ) -> SharedObject<T>
    {
        SharedObject {
            data : Arc::new(Data::new(Box::new(Arc::new(value))))
        }
    }

    //********************************************************************************************
    /// Set the value of the object.
    pub fn set(
        &self,
        value : T
        )
    {
        self.data.set(Box::new(Arc::new(value)));
    }

    //********************************************************************************************
    /// Returns the value of the object.
    pub fn get(&self) -> Box<Arc<T>>
    {
        self.data.get()
    }
}

use std::fmt::{Debug, Display, Formatter, Error};

impl<T : Debug> Debug for SharedObject<T>
{
    //*********************************************************************************************
    /// Implementation of Debug.
    fn fmt(
        &self,
        f : &mut Formatter
        ) -> Result<(), Error>
    {
        write!(f, "{:?}", self.get())
    }
}

impl<T : Display> Display for SharedObject<T>
{
    //*********************************************************************************************
    /// Implementation of Display.
    fn fmt(
        &self,
        f : &mut Formatter
        ) -> Result<(), Error>
    {
        write!(f, "{}", self.get())
    }
}

#[cfg(test)]
mod tests
{
    //*********************************************************************************************
    /// Test that get/set work with only 1 instance.
    #[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 that get/set work with multiple instances.
    #[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");
    }
}