cyclotrace 0.2.0

A high-performance, wait-free ring buffer in Rust. Supports SPMC with peek-based semantics instead of destructive consumption.
Documentation
#![allow(unused_imports)]
use cyclotrace::*;

#[cfg(test)]
#[cfg(feature = "loom")]
mod loom {
    use super::*;
    use ::loom;

    #[test]
    fn test_loom() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 2>();
            let t1 = loom::thread::spawn(move || {
                writer.write(42);
            });
            let r1 = reader.clone();
            let t2 = loom::thread::spawn(move || {
                if let Some(v) = r1.get(0) {
                    assert_eq!(v, 42);
                }
            });
            t1.join().unwrap();
            t2.join().unwrap();
            let v = reader.get(0);
            assert_eq!(v, Some(42));
        });
    }

    #[test]
    fn get_consistency() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 2>();

            let t = loom::thread::spawn(move || {
                writer.write(1);
                writer.write(2);
            });

            if let Some(v) = reader.get(0) {
                assert!(v == 1 || v == 2);
            }

            if let Some(v) = reader.get(1) {
                assert!(v == 1 || v == 2);
            }

            t.join().unwrap();
        });
    }

    #[test]
    fn wraparound() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 2>();

            let t = loom::thread::spawn(move || {
                writer.write(1);
                writer.write(2);
                writer.write(3);
            });

            for i in 0..3 {
                if let Some(v) = reader.get(i) {
                    assert!(v <= 3);
                }
            }

            t.join().unwrap();
        });
    }

    #[test]
    fn multiple_get() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 2>();

            let t = loom::thread::spawn(move || {
                writer.write(42);
            });

            let r1 = reader.clone();
            let t1 = loom::thread::spawn(move || {
                if let Some(v) = r1.get(0) {
                    assert_eq!(v, 42);
                }
            });
            let r2 = reader;
            let t2 = loom::thread::spawn(move || {
                if let Some(v) = r2.get(0) {
                    assert_eq!(v, 42);
                }
            });

            t.join().unwrap();
            t1.join().unwrap();
            t2.join().unwrap();
        });
    }

    #[test]
    fn range_get() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 4>();

            let t1 = loom::thread::spawn(move || {
                writer.write(1);
                writer.write(2);
                writer.write(3);
            });

            let t2 = loom::thread::spawn(move || {
                for _ in 0..3 {
                    let mut buf = Vec::new();
                    if reader.get_range(.., &mut buf).is_some() {
                        assert!(buf.iter().all(|&x| x <= 3));
                    }
                }
            });

            t1.join().unwrap();
            t2.join().unwrap();
        });
    }

    #[test]
    fn read() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 4>();

            let t1 = loom::thread::spawn(move || {
                writer.write(1);
                writer.write(2);
                writer.write(3);
            });

            let t2 = loom::thread::spawn(move || {
                let v = reader.read_latest();

                assert!(v <= 3);
            });

            t1.join().unwrap();
            t2.join().unwrap();
        })
    }

    #[test]
    fn read_range() {
        loom::model(|| {
            let (writer, reader) = create_buffer::<u32, 4>();
            let t1 = loom::thread::spawn(move || {
                writer.write(1);
                writer.write(2);
                writer.write(3);
            });

            let t2 = loom::thread::spawn(move || {
                let mut buf = Vec::new();
                reader.read_range(.., &mut buf).unwrap();
                assert!(buf.iter().all(|&x| x <= 3));
            });

            t1.join().unwrap();
            t2.join().unwrap();
        });
    }
}

#[cfg(test)]
#[cfg(not(feature = "loom"))]
mod tests {
    use super::*;

    #[test]
    fn multiple_get() {
        let (writer, reader) = create_buffer::<u32, 2>();
        writer.write(42);
        assert_eq!(reader.get(0), Some(42));
        assert_eq!(reader.get(0), Some(42));
    }

    #[test]
    fn empty() {
        let (_, reader) = create_buffer::<u32, 2>();
        assert_eq!(reader.get(0), None);
    }

    #[test]
    fn get() {
        let (writer, reader) = create_buffer::<u32, 2>();
        writer.write(42);
        assert_eq!(reader.get(0), Some(42));
        assert_eq!(reader.get(1), None);
    }

    #[test]
    fn wraparound() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        assert_eq!(reader.get(0), Some(3));
        assert_eq!(reader.get(1), Some(2));
    }

    #[test]
    fn range_get() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = Vec::new();
        let _ = reader.get_range(.., &mut buf);
        assert_eq!(buf, vec![1, 2, 3]);
    }

    #[test]
    fn range_get_wraparound() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        writer.write(4);
        writer.write(5);
        let mut buf = Vec::new();
        let _ = reader.get_range(.., &mut buf);
        assert_eq!(buf, vec![3, 4, 5]);
    }

    #[test]
    fn range_get_empty() {
        let (_writer, reader) = create_buffer::<u32, 2>();
        let mut buf = Vec::new();
        let _ = reader.get_range(.., &mut buf);
        assert!(buf.is_empty());
    }

    #[test]
    fn range_get_slice() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = [0; 3];
        let _ = reader.get_range(.., buf.as_mut_slice());
        assert_eq!(buf, [1, 2, 3]);
    }

    #[test]
    fn range_get_array() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = [0; 3];
        let _ = reader.get_range(.., &mut buf);
        assert_eq!(buf, [1, 2, 3]);
    }

    #[test]
    fn read_latest() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(42);
        assert_eq!(reader.read_latest(), 42);
    }

    #[test]
    fn read_latest_across_threads() {
        let (writer, reader) = create_buffer::<u32, 4>();

        let t = std::thread::spawn(move || {
            writer.write(42);
        });

        assert_eq!(reader.read_latest(), 42);

        t.join().unwrap();
    }

    #[test]
    fn read_range() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = Vec::new();
        reader.read_range(.., &mut buf).unwrap();
        assert_eq!(buf, vec![1, 2, 3]);
    }

    #[test]
    fn read_overwrite() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        writer.write(4);
        writer.write(5);
        let mut buf = Vec::new();
        reader.read_range(.., &mut buf).unwrap();
        assert_eq!(buf, vec![3, 4, 5]);
    }

    #[cfg(feature = "arrayvec")]
    #[test]
    fn range_get_arrayvec() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = arrayvec::ArrayVec::<u32, 3>::new();
        let _ = reader.get_range(.., &mut buf);
        assert_eq!(buf, arrayvec::ArrayVec::<u32, 3>::from([1, 2, 3]));
    }

    #[cfg(feature = "heapless")]
    #[test]
    fn range_get_heapless() {
        let (writer, reader) = create_buffer::<u32, 4>();
        writer.write(1);
        writer.write(2);
        writer.write(3);
        let mut buf = heapless::Vec::<u32, 3>::new();
        let _ = reader.get_range(.., &mut buf);
        assert_eq!(buf, heapless::Vec::<u32, 3>::from([1, 2, 3]));
    }
}