per-thread-object 0.6.1

Efficient per-object thread-local storage implementation
Documentation
#[cfg(not(feature = "loom"))]
#[cfg(not(feature = "shuttle"))]
mod loom {
    pub use std::thread;
    pub use std::sync;

    pub fn model<F>(f: F)
    where
        F: Fn() + Sync + Send + 'static
    {
        f()
    }
}

#[cfg(feature = "shuttle")]
mod loom {
    pub use shuttle::thread;
    pub use shuttle::sync;

    pub fn model<F>(f: F)
    where
        F: Fn() + Sync + Send + 'static
    {
        shuttle::check_random(f, 1000)
    }
}

use loom::thread;
use loom::sync::Arc;
use per_thread_object::ThreadLocal;


#[test]
fn test_get() {
    loom::model(|| {
        let tl: ThreadLocal<Box<usize>> = ThreadLocal::new();
        let tl = Arc::new(tl);

        per_thread_object::stack_token!(token);

        assert!(tl.get(token).is_none());

        let val = **tl.get_or_init(token, || Box::new(0x42));
        assert_eq!(0x42, val);

        let tl2 = tl.clone();
        let j = thread::spawn(move || {
            per_thread_object::stack_token!(token);

            let val = **tl2.get_or_init(token, || Box::new(0x22));
            assert_eq!(0x22, val);
        });

        let val = **tl.get(token).unwrap();
        assert_eq!(0x42, val);

        let val = **tl.get_or_init(token, || Box::new(0x32));
        assert_eq!(0x42, val);

        j.join().unwrap();
    });
}

#[test]
fn test_thread_get() {
    loom::model(|| {
        let tl: Arc<ThreadLocal<Box<usize>>> = Arc::new(ThreadLocal::new());
        let tl2 = tl.clone();
        let tl3 = tl2.clone();

        per_thread_object::stack_token!(token);

        let val = **tl.get_or_init(token, || Box::new(0x42));
        assert_eq!(0x42, val);

        let j = thread::spawn(move || {
            per_thread_object::stack_token!(token);

            assert!(tl2.get(token).is_none());

            let val = **tl2.get_or_init(token, || Box::new(0x32));
            assert_eq!(0x32, val);

            let val = **tl2.get_or_init(token, || Box::new(0x12));
            assert_eq!(0x32, val);
        });

        let j2 = thread::spawn(move || {
            let tl3 = tl3;

            per_thread_object::stack_token!(token);

            assert!(tl3.get(token).is_none());

            let val = **tl3.get_or_init(token, || Box::new(0x22));
            assert_eq!(0x22, val);

            let val = **tl3.get(token).unwrap();
            assert_eq!(0x22, val);
        });

        let val = **tl.get_or_init(token, || Box::new(0x42));
        assert_eq!(0x42, val);

        j.join().unwrap();
        j2.join().unwrap();
    });
}

#[test]
fn test_multi_obj() {
    loom::model(|| {
        let tla: ThreadLocal<Box<u32>> = ThreadLocal::new();
        let tlb: ThreadLocal<Box<u64>> = ThreadLocal::new();
        let tla = Arc::new(tla);
        let tlb = Arc::new(tlb);

        per_thread_object::stack_token!(token);

        assert!(tla.get(token).is_none());
        assert!(tlb.get(token).is_none());

        let tla1 = tla.clone();
        let tlb1: Arc<ThreadLocal<_>> = tlb.clone();

        let j = thread::spawn(move || {
            let tla1 = tla1;

            per_thread_object::stack_token!(token);

            assert!(tla1.get(token).is_none());

            let val = **tla1.get_or_init(token, || Box::new(0x32));
            assert_eq!(0x32, val);

            let val = **tla1.get(token).unwrap();
            assert_eq!(0x32, val);
        });

        let j2 = thread::spawn(move || {
            per_thread_object::stack_token!(token);

            assert!(tlb1.get(token).is_none());

            let val = **tlb1.get_or_init(token, || Box::new(0x22));
            assert_eq!(0x22, val);

            let val = **tlb1.get(token).unwrap();
            assert_eq!(0x22, val);
        });

        let val = **tla.get_or_init(token, || Box::new(0x42));
        assert_eq!(0x42, val);
        let val = **tlb.get_or_init(token, || Box::new(0x52));
        assert_eq!(0x52, val);

        j.join().unwrap();
        j2.join().unwrap();

        let val = **tla.get(token).unwrap();
        assert_eq!(0x42, val);
        let val = **tlb.get(token).unwrap();
        assert_eq!(0x52, val);
    });
}

#[test]
fn test_more_thread() {
    loom::model(|| {
        let tla: ThreadLocal<Box<u32>> = ThreadLocal::new();
        let tlb: ThreadLocal<Box<u64>> = ThreadLocal::new();
        let tla = Arc::new(tla);
        let tlb = Arc::new(tlb);

        #[cfg(not(feature = "loom"))]
        let n = 33;

        #[cfg(feature = "loom")]
        let n = 3;

        let handles = (0..n)
            .map(|i| {
                let tla = tla.clone();
                let tlb = tlb.clone();

                thread::spawn(move || {
                    per_thread_object::stack_token!(token);

                    let val = **tla.get_or_init(token, || Box::new(i as u32));
                    assert_eq!(i as u32, val);

                    let val = **tlb.get_or_init(token, || Box::new(i as u64));
                    assert_eq!(i as u64, val);
                })
            })
            .collect::<Vec<_>>();

        for h in handles {
            h.join().unwrap();
        }
    });
}

#[test]
#[cfg(not(feature = "loom"))]
#[cfg(not(feature = "shuttle"))]
fn test_loop_thread() {
    use std::sync::Barrier;
    use per_thread_object::ThreadLocal;

    let tl: ThreadLocal<u64> = ThreadLocal::new();
    let bar = Barrier::new(64);

    std::thread::scope(|s| {
        for _ in 0..64 { // must > DEFAULT_PAGE_CAP
            s.spawn(|| {
                per_thread_object::stack_token!(token);

                tl.get_or_init(token, || 0x42);

                bar.wait();
            });
        }
    });
}