2 releases

0.1.1 Feb 10, 2022
0.1.0 Feb 10, 2022

#2 in #loom

Custom license

6KB

loomy

Crates.io [Documentation][docs]

A shim crate to easily test code with loom.

// std or loom, chosen at compile time by crate feature.
use loomy::{thread, cell::UnsafeCell};

struct Foo {
    cell: UnsafeCell<...>,
}

#[test]
fn test_example() {
    // When using `std`, `loomy::model` only invokes the closure and nothing
    // more.
    loomy::model(|| {
        // ...
        thread::spawn(|| {
            // ...
        });
        // ...
    });
}

Run tests with std:

$ cargo test

Run tests with loom:

$ cargo test --features loomy/enable

lib.rs:

A shim crate to easily test code with loom.

Import common types and modules like UnsafeCell, thread, Arc, AtomicI32, etc from this crate, and run loom tests from the command line with cargo test --features loomy/enable.

Example

The following module can be tested in two ways:

$ cargo test
$ cargo test --features loomy/enable

When loomy/enable is set, then the code will be tested as a loomy model, otherwise all types default to their std equivalents, and the code will be tested as normal.

// Note the use of `loomy` instead of `std` or `loom`.
use loomy::{
    hint,
    cell::UnsafeCell,
    sync::atomic::{AtomicBool, Ordering},
};

pub struct SpinLock<T> {
    flag: AtomicBool,
    data: UnsafeCell<T>,
}

unsafe impl<T> Send for SpinLock<T> {}
unsafe impl<T> Sync for SpinLock<T> {}

impl<T> SpinLock<T> {
    pub fn new(t: T) -> Self {
        Self {
            flag: AtomicBool::new(false),
            data: UnsafeCell::new(t),
        }
    }

    pub fn with<R, F: FnOnce(&mut T) -> R>(&self, f: F) -> R {
        while let Err(_) = self
            .flag
            .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
        {
            hint::spin_loop()
        }

        let out = self.data.with_mut(move |t| unsafe { f(&mut *t) });
        self.flag.store(false, Ordering::Release);
        out
    }
}

#[cfg(test)]
mod tests {
    // Also using `loomy` instead of `loom` or `std`.
    use loomy::{thread, sync::Arc};
    # mod tmp {
    use super::*;
    # }

    #[test]
    # fn mock() {}
    fn test_simple() {
        loomy::model(|| {
            let lock = Arc::new(SpinLock::new(123));
            let lock2 = Arc::clone(&lock);

            let t = thread::spawn(move || {
                lock2.with(|n| *n += 1);
            });

            lock.with(|n| *n = 456);

            let out = lock.with(|n| *n);

            t.join().unwrap();

            assert!(out == 456 || out == 457);
        });
    }
    # mod dummy {
}

A note on UnsafeCell

UnsafeCell in loom has a closure-based API. When using std types, UnsafeCell is wrapped in order to provide the same API.

Dependencies

~0–29MB
~360K SLoC