makepad/libs/relax/compiler/std/check/tests/mpsc.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

224 lines
8.6 KiB
Rust

//! Channel algorithms mounted against real std, including fixed-seed differential streams.
#![allow(dead_code, unused_imports)]
extern crate alloc;
pub use std::{sync, thread, time, process};
#[path = "../../std/sync/mpmc/mod.rs"]
mod mpmc;
#[path = "../../std/sync/mpsc.rs"]
mod mpsc;
use std::sync::{Arc, Barrier};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
struct Counted { id: usize, counts: Arc<Vec<AtomicUsize>> }
impl Drop for Counted {
fn drop(&mut self) { self.counts[self.id].fetch_add(1, Ordering::SeqCst); }
}
fn counts(n: usize) -> Arc<Vec<AtomicUsize>> {
let mut v = Vec::new();
for _ in 0..n { v.push(AtomicUsize::new(0)); }
Arc::new(v)
}
fn exactly_once(c: &Arc<Vec<AtomicUsize>>) -> bool {
c.iter().all(|n| n.load(Ordering::SeqCst) == 1)
}
#[test]
fn a01_many_senders() {
// Each producer has a deterministic xorshift stream; arrival order is free.
for flavor in 0..4 {
let mut hs = Vec::new();
let rx;
if flavor == 0 {
let (tx, r) = mpsc::channel();
rx = r;
for sender in 0..6u64 {
let tx = tx.clone();
hs.push(thread::spawn(move || {
let mut x = sender + 123;
for i in 0..1200u64 {
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
tx.send((sender, i, x)).unwrap();
}
}));
}
drop(tx);
} else {
let cap = if flavor == 1 { 0 } else if flavor == 2 { 1 } else { 37 };
let (tx, r) = mpsc::sync_channel(cap);
rx = r;
for sender in 0..6u64 {
let tx = tx.clone();
hs.push(thread::spawn(move || {
let mut x = sender + 123;
for i in 0..1200u64 {
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
tx.send((sender, i, x)).unwrap();
}
}));
}
drop(tx);
}
let mut seq = [0u64; 6];
let mut state = [123u64, 124, 125, 126, 127, 128];
for (s, i, x) in rx {
let s = s as usize;
assert_eq!(seq[s], i); seq[s] += 1;
state[s] ^= state[s] << 13; state[s] ^= state[s] >> 7; state[s] ^= state[s] << 17;
assert_eq!(state[s], x);
}
for h in hs { h.join().unwrap(); }
assert_eq!(seq, [1200; 6]);
println!("| many flavor {} exact order and values", flavor);
}
}
#[test]
fn a02_disconnect_blocked_senders() {
for cap in [0usize, 1, 7] {
let c = counts(cap + 8);
let (tx, rx) = mpsc::sync_channel(cap);
for id in 0..cap { tx.send(Counted { id, counts: c.clone() }).ok().unwrap(); }
let barrier = Arc::new(Barrier::new(9));
let mut hs = Vec::new();
for i in 0..8 {
let tx = tx.clone(); let c = c.clone(); let b = barrier.clone();
hs.push(thread::spawn(move || {
b.wait();
match tx.send(Counted { id: cap + i, counts: c }) {
Err(e) => { assert_eq!(e.0.id, cap + i); drop(e); }
Ok(()) => panic!("send must disconnect"),
}
}));
}
barrier.wait(); thread::sleep(Duration::from_millis(10));
drop(rx);
for h in hs { h.join().unwrap(); }
drop(tx);
assert!(exactly_once(&c));
println!("| blocked send cap {} returned values dropped once", cap);
}
}
#[test]
fn a03_disconnect_receiver_and_timeouts() {
for flavor in 0..3 {
let (start, ready) = mpsc::channel();
let h;
if flavor == 0 {
let (tx, rx) = mpsc::channel::<usize>();
assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(mpsc::RecvTimeoutError::Timeout));
h = thread::spawn(move || { start.send(()).unwrap(); assert!(rx.recv().is_err()); });
ready.recv().unwrap(); thread::sleep(Duration::from_millis(5)); drop(tx);
} else {
let (tx, rx) = mpsc::sync_channel::<usize>(flavor - 1);
assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Empty));
assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(mpsc::RecvTimeoutError::Timeout));
h = thread::spawn(move || { start.send(()).unwrap(); assert!(rx.recv().is_err()); });
ready.recv().unwrap(); thread::sleep(Duration::from_millis(5)); drop(tx);
}
h.join().unwrap();
println!("| empty timeout then disconnected flavor {}", flavor);
}
for cap in [0usize, 1, 4] {
let (tx, rx) = mpsc::sync_channel(cap);
for n in 0..cap { assert_eq!(tx.try_send(n), Ok(())); }
assert_eq!(tx.try_send(99), Err(mpsc::TrySendError::Full(99)));
for n in 0..cap { assert_eq!(rx.try_recv(), Ok(n)); }
drop(rx);
assert_eq!(tx.try_send(100), Err(mpsc::TrySendError::Disconnected(100)));
let (tx, rx) = mpsc::sync_channel::<usize>(cap); drop(tx);
assert_eq!(rx.try_recv(), Err(mpsc::TryRecvError::Disconnected));
assert_eq!(rx.recv_timeout(Duration::from_secs(1)), Err(mpsc::RecvTimeoutError::Disconnected));
println!("| try full empty disconnected cap {}", cap);
}
// Successful nonblocking rendezvous with a receiver parked or about to park.
let (tx, rx) = mpsc::sync_channel(0);
let h = thread::spawn(move || rx.recv_timeout(Duration::from_secs(5)).unwrap());
loop {
match tx.try_send(73) {
Ok(()) => break,
Err(mpsc::TrySendError::Full(_)) => thread::yield_now(),
Err(_) => panic!("receiver disconnected"),
}
}
assert_eq!(h.join().unwrap(), 73);
println!("| rendezvous try_send delivered");
}
#[test]
fn a04_drop_and_iterators() {
for flavor in 0..3 {
let c = counts(200);
if flavor == 0 {
let (tx, rx) = mpsc::channel();
for id in 0..200 { tx.send(Counted { id, counts: c.clone() }).ok().unwrap(); }
for _ in 0..31 { drop(rx.recv().unwrap()); }
drop(rx); drop(tx);
} else {
let (tx, rx) = mpsc::sync_channel(if flavor == 1 { 0 } else { 37 });
let cc = c.clone();
let h = thread::spawn(move || {
for id in 0..200 { let _ = tx.send(Counted { id, counts: cc.clone() }); }
});
for _ in 0..31 { drop(rx.recv().unwrap()); }
drop(rx); h.join().unwrap();
}
assert!(exactly_once(&c));
println!("| drop flavor {} exactly once", flavor);
}
let (tx, rx) = mpsc::channel();
for n in 0..10 { tx.send(n).unwrap(); }
assert_eq!(rx.try_iter().sum::<i32>(), 45);
assert_eq!(rx.try_iter().next(), None);
for n in 0..10 { tx.send(n).unwrap(); }
drop(tx); assert_eq!(rx.iter().sum::<i32>(), 45);
let (tx, rx) = mpsc::channel();
for n in 0..10 { tx.send(n).unwrap(); }
drop(tx); assert_eq!(rx.into_iter().sum::<i32>(), 45);
println!("| iter try_iter into_iter sums 45");
}
#[test]
fn randomized_against_real_std() {
for cap in [1usize, 2, 7, 31, 32, 65] {
let (tx, rx) = mpsc::sync_channel(cap);
let (real_tx, real_rx) = std::sync::mpsc::sync_channel(cap);
let mut x = 0x123456789abcdefu64;
for _ in 0..20000 {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
if x & 1 == 0 {
let a = tx.try_send(x);
let b = real_tx.try_send(x);
assert_eq!(format!("{:?}", a), format!("{:?}", b));
match (a, b) {
(Err(mpsc::TrySendError::Full(a)), Err(std::sync::mpsc::TrySendError::Full(b))) => assert_eq!(a, b),
(Ok(()), Ok(())) => (),
_ => panic!("different send results"),
}
} else {
assert_eq!(format!("{:?}", rx.try_recv()), format!("{:?}", real_rx.try_recv()));
}
}
drop(tx); drop(real_tx);
assert_eq!(rx.into_iter().collect::<Vec<_>>(), real_rx.into_iter().collect::<Vec<_>>());
}
let (tx, rx) = mpsc::channel();
let (real_tx, real_rx) = std::sync::mpsc::channel();
let mut x = 0x3456789abcdefu64;
for _ in 0..20000 {
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
if x & 3 != 0 {
tx.send(x).unwrap(); real_tx.send(x).unwrap();
} else {
assert_eq!(format!("{:?}", rx.try_recv()), format!("{:?}", real_rx.try_recv()));
}
}
drop(tx); drop(real_tx);
assert_eq!(rx.into_iter().collect::<Vec<_>>(), real_rx.into_iter().collect::<Vec<_>>());
}