Expand description
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};
use super::*;
#[test]
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);
});
}
}
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.
Modules
Memory allocation APIs.
Hints to compiler that affects how code should be emitted or optimized. Hints may be compile time or runtime.
Useful synchronization primitives.
Native threads.