arc-buffer 0.1.0

A Clone-on-write reference-counter vector for Copy types.
Documentation
use crate::parallel;
use crate::{arc_buf, ArcBuffer};
use core::mem;
use core::sync::atomic::AtomicBool;
use core::sync::atomic::Ordering::*;

macro_rules! dropflag {
    ($(static $($name:ident),+$(,)?;)*) => {
        $($(
            static $name: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
            $name.store(false, core::sync::atomic::Ordering::SeqCst);
        )+)*
    };
}

fn set_drop_hook<T>(buf: &mut ArcBuffer<T>, dropped: &'static AtomicBool) {
    unsafe {
        buf.set_drop_hook(Some(move || {
            dropped
                .compare_exchange(false, true, SeqCst, SeqCst)
                .expect("dropped twice!");
        }));
    }
}

fn with_drop_hook<T>(mut buf: ArcBuffer<T>, dropped: &'static AtomicBool) -> ArcBuffer<T> {
    set_drop_hook(&mut buf, dropped);
    buf
}

#[test]
fn drop_no_clones() {
    dropflag! { static DROPPED; }

    let buf = with_drop_hook(arc_buf![1, 2, 3], &DROPPED);
    assert_eq!(DROPPED.load(SeqCst), false);
    mem::drop(buf);
    assert_eq!(DROPPED.load(SeqCst), true);
}

#[test]
fn drop_clone_first() {
    dropflag! { static DROPPED; }

    let buf1 = with_drop_hook(arc_buf![1, 2, 3], &DROPPED);
    assert_eq!(DROPPED.load(SeqCst), false);
    let buf2 = buf1.clone();
    mem::drop(buf2);
    assert_eq!(DROPPED.load(SeqCst), false);
    mem::drop(buf1);
    assert_eq!(DROPPED.load(SeqCst), true);
}

#[test]
fn drop_clone_last() {
    dropflag! { static DROPPED; }

    let buf1 = with_drop_hook(arc_buf![1, 2, 3], &DROPPED);
    assert_eq!(DROPPED.load(SeqCst), false);
    let buf2 = buf1.clone();
    mem::drop(buf1);
    assert_eq!(DROPPED.load(SeqCst), false);
    mem::drop(buf2);
    assert_eq!(DROPPED.load(SeqCst), true);
}

#[test]
fn drop_after_mutating_clone() {
    dropflag! { static D1, D2; }

    let buf1 = with_drop_hook(arc_buf![1, 2, 3], &D1);
    assert_eq!(D1.load(SeqCst), false);
    let mut buf2 = buf1.clone();
    assert_eq!(D1.load(SeqCst), false);
    buf2.push(4);
    set_drop_hook(&mut buf2, &D2);
    mem::drop(buf1);
    assert_eq!(D1.load(SeqCst), true);
    assert_eq!(D2.load(SeqCst), false);
    mem::drop(buf2);
    assert_eq!(D1.load(SeqCst), true);
    assert_eq!(D2.load(SeqCst), true);
}

#[test]
fn drop_after_mutating_original() {
    dropflag! { static D1, D2; }

    let mut buf1 = with_drop_hook(arc_buf![1, 2, 3], &D1);
    assert_eq!(D1.load(SeqCst), false);
    let buf2 = buf1.clone();
    assert_eq!(D1.load(SeqCst), false);
    buf1.push(4);
    set_drop_hook(&mut buf1, &D2);
    mem::drop(buf2);
    assert_eq!(D1.load(SeqCst), true);
    assert_eq!(D2.load(SeqCst), false);
    mem::drop(buf1);
    assert_eq!(D1.load(SeqCst), true);
    assert_eq!(D2.load(SeqCst), true);
}

#[test]
fn drop_after_mutating_clone_parallel() {
    dropflag! { static D1, D2, D3; }

    let buf = with_drop_hook(arc_buf![1, 2, 3, 4], &D1);
    let buf2 = buf.clone();

    parallel! {
        [clone mut buf] {
            buf.push(4);
            set_drop_hook(&mut buf, &D2);
            assert_eq!(D2.load(SeqCst), false);
            assert_eq!(buf, [1, 2, 3, 4, 4]);
            mem::drop(buf);
            assert_eq!(D2.load(SeqCst), true);
        };
        [clone mut buf] {
            buf.push(5);
            set_drop_hook(&mut buf, &D3);
            assert_eq!(D3.load(SeqCst), false);
            assert_eq!(buf, [1, 2, 3, 4, 5]);
            mem::drop(buf);
            assert_eq!(D3.load(SeqCst), true);
        };
    }

    assert_eq!(buf, [1, 2, 3, 4]);
    assert_eq!(buf2, [1, 2, 3, 4]);
    assert_eq!(D1.load(SeqCst), false);
    assert_eq!(D2.load(SeqCst), true);
    assert_eq!(D3.load(SeqCst), true);
    mem::drop(buf);
    assert_eq!(buf2, [1, 2, 3, 4]);
    assert_eq!(D1.load(SeqCst), false);
    mem::drop(buf2);
    assert_eq!(D1.load(SeqCst), true);
}