You've already forked interchange
mirror of
https://github.com/trussed-dev/interchange.git
synced 2026-03-11 16:31:34 -07:00
124 lines
3.6 KiB
Rust
124 lines
3.6 KiB
Rust
#[cfg(loom)]
|
|
use loom::thread;
|
|
#[cfg(not(loom))]
|
|
use std::thread;
|
|
|
|
use std::mem::drop;
|
|
|
|
use interchange::{Channel, Requester, Responder};
|
|
#[cfg(loom)]
|
|
use std::sync::atomic::Ordering::Acquire;
|
|
use std::sync::atomic::{AtomicBool, Ordering::Release};
|
|
|
|
static BRANCHES_USED: [AtomicBool; 14] = {
|
|
#[allow(clippy::declare_interior_mutable_const)]
|
|
const ATOMIC_BOOL_INIT: AtomicBool = AtomicBool::new(false);
|
|
[ATOMIC_BOOL_INIT; 14]
|
|
};
|
|
|
|
#[cfg(loom)]
|
|
#[test]
|
|
fn loom_interchange() {
|
|
loom::model(test_function);
|
|
|
|
// Verify that the model explored the expected branches
|
|
for b in &BRANCHES_USED {
|
|
assert!(b.load(Acquire));
|
|
}
|
|
}
|
|
|
|
// This is tested even with the standard library to ensure that the Send/Sync traits are implemented as necessary
|
|
// Loom's thread::spawn doesn't require the function to be `Send`
|
|
#[cfg_attr(not(loom), test)]
|
|
fn test_function() {
|
|
// thread closures must be 'static
|
|
let channel = Box::leak(Box::new(Channel::new()));
|
|
let dropper = unsafe { Box::from_raw(channel as _) };
|
|
|
|
let (rq, rp) = channel.split().unwrap();
|
|
|
|
let handle1 = thread::spawn(move || requester_thread(rq));
|
|
let handle2 = thread::spawn(move || responder_thread(rp));
|
|
let res1 = handle1.join();
|
|
let res2 = handle2.join();
|
|
|
|
// Avoid memory leak
|
|
drop(dropper);
|
|
|
|
res1.unwrap();
|
|
res2.unwrap();
|
|
}
|
|
|
|
fn requester_thread(mut requester: Requester<'static, u64, u64>) -> Option<()> {
|
|
requester.request(53).unwrap();
|
|
match requester.cancel() {
|
|
Ok(Some(53) | None) => {
|
|
BRANCHES_USED[0].store(true, Release);
|
|
return None;
|
|
}
|
|
Ok(_) => panic!("Invalid state"),
|
|
Err(_) => {
|
|
BRANCHES_USED[1].store(true, Release);
|
|
}
|
|
}
|
|
requester
|
|
.with_response(|r| {
|
|
BRANCHES_USED[2].store(true, Release);
|
|
assert_eq!(*r, 63)
|
|
})
|
|
.ok()
|
|
.or_else(|| {
|
|
BRANCHES_USED[3].store(true, Release);
|
|
None
|
|
})?;
|
|
requester.with_response(|r| assert_eq!(*r, 63)).unwrap();
|
|
requester.take_response().unwrap();
|
|
requester.with_request_mut(|r| *r = 51).unwrap();
|
|
requester.send_request().unwrap();
|
|
thread::yield_now();
|
|
match requester.cancel() {
|
|
Ok(Some(51) | None) => BRANCHES_USED[4].store(true, Release),
|
|
Ok(_) => panic!("Invalid state"),
|
|
Err(_) => {
|
|
BRANCHES_USED[5].store(true, Release);
|
|
match requester.take_response() {
|
|
Some(i) => {
|
|
assert_eq!(i, 79);
|
|
BRANCHES_USED[6].store(true, Release);
|
|
}
|
|
None => BRANCHES_USED[7].store(true, Release),
|
|
}
|
|
}
|
|
}
|
|
BRANCHES_USED[8].store(true, Release);
|
|
None
|
|
}
|
|
|
|
fn responder_thread(mut responder: Responder<'static, u64, u64>) -> Option<()> {
|
|
let req = responder.take_request().or_else(|| {
|
|
BRANCHES_USED[9].store(true, Release);
|
|
None
|
|
})?;
|
|
assert_eq!(req, 53);
|
|
responder.respond(req + 10).ok().or_else(|| {
|
|
BRANCHES_USED[10].store(true, Release);
|
|
None
|
|
})?;
|
|
thread::yield_now();
|
|
responder
|
|
.with_request(|r| {
|
|
BRANCHES_USED[11].store(true, Release);
|
|
assert_eq!(*r, 51)
|
|
})
|
|
.map(|_| assert!(responder.with_request(|_| {}).is_err()))
|
|
.or_else(|_| {
|
|
BRANCHES_USED[12].store(true, Release);
|
|
responder.acknowledge_cancel()
|
|
})
|
|
.ok()?;
|
|
responder.with_response_mut(|r| *r = 79).ok();
|
|
responder.send_response().ok();
|
|
BRANCHES_USED[13].store(true, Release);
|
|
None
|
|
}
|