#[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 }