Source code

Revision control

Copy as Markdown

Other Tools

//! Tests for channel selection using the `Select` struct.
#![allow(clippy::drop_copy)]
use std::any::Any;
use std::cell::Cell;
use std::thread;
use std::time::{Duration, Instant};
use crossbeam_channel::{after, bounded, tick, unbounded, Receiver, Select, TryRecvError};
use crossbeam_utils::thread::scope;
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke1() {
let (s1, r1) = unbounded::<usize>();
let (s2, r2) = unbounded::<usize>();
s1.send(1).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select();
match oper.index() {
i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)),
i if i == oper2 => panic!(),
_ => unreachable!(),
}
s2.send(2).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select();
match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)),
_ => unreachable!(),
}
}
#[test]
fn smoke2() {
let (_s1, r1) = unbounded::<i32>();
let (_s2, r2) = unbounded::<i32>();
let (_s3, r3) = unbounded::<i32>();
let (_s4, r4) = unbounded::<i32>();
let (s5, r5) = unbounded::<i32>();
s5.send(5).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper3 = sel.recv(&r3);
let oper4 = sel.recv(&r4);
let oper5 = sel.recv(&r5);
let oper = sel.select();
match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => panic!(),
i if i == oper3 => panic!(),
i if i == oper4 => panic!(),
i if i == oper5 => assert_eq!(oper.recv(&r5), Ok(5)),
_ => unreachable!(),
}
}
#[test]
fn disconnected() {
let (s1, r1) = unbounded::<i32>();
let (s2, r2) = unbounded::<i32>();
scope(|scope| {
scope.spawn(|_| {
drop(s1);
thread::sleep(ms(500));
s2.send(5).unwrap();
});
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r1).is_err()),
i if i == oper2 => panic!(),
_ => unreachable!(),
},
}
r2.recv().unwrap();
})
.unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r1).is_err()),
i if i == oper2 => panic!(),
_ => unreachable!(),
},
}
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(500));
drop(s2);
});
let mut sel = Select::new();
let oper1 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r2).is_err()),
_ => unreachable!(),
},
}
})
.unwrap();
}
#[test]
fn default() {
let (s1, r1) = unbounded::<i32>();
let (s2, r2) = unbounded::<i32>();
let mut sel = Select::new();
let _oper1 = sel.recv(&r1);
let _oper2 = sel.recv(&r2);
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(_) => panic!(),
}
drop(s1);
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.try_select();
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r1).is_err()),
i if i == oper2 => panic!(),
_ => unreachable!(),
},
}
s2.send(2).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r2);
let oper = sel.try_select();
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert_eq!(oper.recv(&r2), Ok(2)),
_ => unreachable!(),
},
}
let mut sel = Select::new();
let _oper1 = sel.recv(&r2);
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(_) => panic!(),
}
let mut sel = Select::new();
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(_) => panic!(),
}
}
#[test]
fn timeout() {
let (_s1, r1) = unbounded::<i32>();
let (s2, r2) = unbounded::<i32>();
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(1500));
s2.send(2).unwrap();
});
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => panic!(),
_ => unreachable!(),
},
}
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)),
_ => unreachable!(),
},
}
})
.unwrap();
scope(|scope| {
let (s, r) = unbounded::<i32>();
scope.spawn(move |_| {
thread::sleep(ms(500));
drop(s);
});
let mut sel = Select::new();
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => {
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.try_select();
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r).is_err()),
_ => unreachable!(),
},
}
}
Ok(_) => unreachable!(),
}
})
.unwrap();
}
#[test]
fn default_when_disconnected() {
let (_, r) = unbounded::<i32>();
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.try_select();
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r).is_err()),
_ => unreachable!(),
},
}
let (_, r) = unbounded::<i32>();
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.recv(&r).is_err()),
_ => unreachable!(),
},
}
let (s, _) = bounded::<i32>(0);
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.try_select();
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.send(&s, 0).is_err()),
_ => unreachable!(),
},
}
let (s, _) = bounded::<i32>(0);
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => assert!(oper.send(&s, 0).is_err()),
_ => unreachable!(),
},
}
}
#[test]
fn default_only() {
let start = Instant::now();
let mut sel = Select::new();
let oper = sel.try_select();
assert!(oper.is_err());
let now = Instant::now();
assert!(now - start <= ms(50));
let start = Instant::now();
let mut sel = Select::new();
let oper = sel.select_timeout(ms(500));
assert!(oper.is_err());
let now = Instant::now();
assert!(now - start >= ms(450));
assert!(now - start <= ms(550));
}
#[test]
fn unblocks() {
let (s1, r1) = bounded::<i32>(0);
let (s2, r2) = bounded::<i32>(0);
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(500));
s2.send(2).unwrap();
});
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => assert_eq!(oper.recv(&r2), Ok(2)),
_ => unreachable!(),
},
}
})
.unwrap();
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(500));
assert_eq!(r1.recv().unwrap(), 1);
});
let mut sel = Select::new();
let oper1 = sel.send(&s1);
let oper2 = sel.send(&s2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
i if i == oper1 => oper.send(&s1, 1).unwrap(),
i if i == oper2 => panic!(),
_ => unreachable!(),
},
}
})
.unwrap();
}
#[test]
fn both_ready() {
let (s1, r1) = bounded(0);
let (s2, r2) = bounded(0);
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(500));
s1.send(1).unwrap();
assert_eq!(r2.recv().unwrap(), 2);
});
for _ in 0..2 {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.send(&s2);
let oper = sel.select();
match oper.index() {
i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)),
i if i == oper2 => oper.send(&s2, 2).unwrap(),
_ => unreachable!(),
}
}
})
.unwrap();
}
#[test]
fn loop_try() {
const RUNS: usize = 20;
for _ in 0..RUNS {
let (s1, r1) = bounded::<i32>(0);
let (s2, r2) = bounded::<i32>(0);
let (s_end, r_end) = bounded::<()>(0);
scope(|scope| {
scope.spawn(|_| loop {
let mut done = false;
let mut sel = Select::new();
let oper1 = sel.send(&s1);
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
i if i == oper1 => {
let _ = oper.send(&s1, 1);
done = true;
}
_ => unreachable!(),
},
}
if done {
break;
}
let mut sel = Select::new();
let oper1 = sel.recv(&r_end);
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
i if i == oper1 => {
let _ = oper.recv(&r_end);
done = true;
}
_ => unreachable!(),
},
}
if done {
break;
}
});
scope.spawn(|_| loop {
if let Ok(x) = r2.try_recv() {
assert_eq!(x, 2);
break;
}
let mut done = false;
let mut sel = Select::new();
let oper1 = sel.recv(&r_end);
let oper = sel.try_select();
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
i if i == oper1 => {
let _ = oper.recv(&r_end);
done = true;
}
_ => unreachable!(),
},
}
if done {
break;
}
});
scope.spawn(|_| {
thread::sleep(ms(500));
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.send(&s2);
let oper = sel.select_timeout(ms(1000));
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
i if i == oper1 => assert_eq!(oper.recv(&r1), Ok(1)),
i if i == oper2 => assert!(oper.send(&s2, 2).is_ok()),
_ => unreachable!(),
},
}
drop(s_end);
});
})
.unwrap();
}
}
#[test]
fn cloning1() {
scope(|scope| {
let (s1, r1) = unbounded::<i32>();
let (_s2, r2) = unbounded::<i32>();
let (s3, r3) = unbounded::<()>();
scope.spawn(move |_| {
r3.recv().unwrap();
drop(s1.clone());
assert!(r3.try_recv().is_err());
s1.send(1).unwrap();
r3.recv().unwrap();
});
s3.send(()).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select();
match oper.index() {
i if i == oper1 => drop(oper.recv(&r1)),
i if i == oper2 => drop(oper.recv(&r2)),
_ => unreachable!(),
}
s3.send(()).unwrap();
})
.unwrap();
}
#[test]
fn cloning2() {
let (s1, r1) = unbounded::<()>();
let (s2, r2) = unbounded::<()>();
let (_s3, _r3) = unbounded::<()>();
scope(|scope| {
scope.spawn(move |_| {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select();
match oper.index() {
i if i == oper1 => panic!(),
i if i == oper2 => drop(oper.recv(&r2)),
_ => unreachable!(),
}
});
thread::sleep(ms(500));
drop(s1.clone());
s2.send(()).unwrap();
})
.unwrap();
}
#[test]
fn preflight1() {
let (s, r) = unbounded();
s.send(()).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
i if i == oper1 => drop(oper.recv(&r)),
_ => unreachable!(),
}
}
#[test]
fn preflight2() {
let (s, r) = unbounded();
drop(s.clone());
s.send(()).unwrap();
drop(s);
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
i if i == oper1 => assert_eq!(oper.recv(&r), Ok(())),
_ => unreachable!(),
}
assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected));
}
#[test]
fn preflight3() {
let (s, r) = unbounded();
drop(s.clone());
s.send(()).unwrap();
drop(s);
r.recv().unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
i if i == oper1 => assert!(oper.recv(&r).is_err()),
_ => unreachable!(),
}
}
#[test]
fn duplicate_operations() {
let (s, r) = unbounded::<i32>();
let hit = vec![Cell::new(false); 4];
while hit.iter().map(|h| h.get()).any(|hit| !hit) {
let mut sel = Select::new();
let oper0 = sel.recv(&r);
let oper1 = sel.recv(&r);
let oper2 = sel.send(&s);
let oper3 = sel.send(&s);
let oper = sel.select();
match oper.index() {
i if i == oper0 => {
assert!(oper.recv(&r).is_ok());
hit[0].set(true);
}
i if i == oper1 => {
assert!(oper.recv(&r).is_ok());
hit[1].set(true);
}
i if i == oper2 => {
assert!(oper.send(&s, 0).is_ok());
hit[2].set(true);
}
i if i == oper3 => {
assert!(oper.send(&s, 0).is_ok());
hit[3].set(true);
}
_ => unreachable!(),
}
}
}
#[test]
fn nesting() {
let (s, r) = unbounded::<i32>();
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
assert!(oper.send(&s, 0).is_ok());
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
assert_eq!(oper.recv(&r), Ok(0));
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
assert!(oper.send(&s, 1).is_ok());
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
assert_eq!(oper.recv(&r), Ok(1));
}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
_ => unreachable!(),
}
}
#[test]
fn stress_recv() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = unbounded();
let (s2, r2) = bounded(5);
let (s3, r3) = bounded(100);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
s1.send(i).unwrap();
r3.recv().unwrap();
s2.send(i).unwrap();
r3.recv().unwrap();
}
});
for i in 0..COUNT {
for _ in 0..2 {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)),
ix if ix == oper2 => assert_eq!(oper.recv(&r2), Ok(i)),
_ => unreachable!(),
}
s3.send(()).unwrap();
}
}
})
.unwrap();
}
#[test]
fn stress_send() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded(0);
let (s2, r2) = bounded(0);
let (s3, r3) = bounded(100);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
assert_eq!(r1.recv().unwrap(), i);
assert_eq!(r2.recv().unwrap(), i);
r3.recv().unwrap();
}
});
for i in 0..COUNT {
for _ in 0..2 {
let mut sel = Select::new();
let oper1 = sel.send(&s1);
let oper2 = sel.send(&s2);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert!(oper.send(&s1, i).is_ok()),
ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()),
_ => unreachable!(),
}
}
s3.send(()).unwrap();
}
})
.unwrap();
}
#[test]
fn stress_mixed() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded(0);
let (s2, r2) = bounded(0);
let (s3, r3) = bounded(100);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
s1.send(i).unwrap();
assert_eq!(r2.recv().unwrap(), i);
r3.recv().unwrap();
}
});
for i in 0..COUNT {
for _ in 0..2 {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.send(&s2);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)),
ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()),
_ => unreachable!(),
}
}
s3.send(()).unwrap();
}
})
.unwrap();
}
#[test]
fn stress_timeout_two_threads() {
const COUNT: usize = 20;
let (s, r) = bounded(2);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(500));
}
loop {
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.select_timeout(ms(100));
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
ix if ix == oper1 => {
assert!(oper.send(&s, i).is_ok());
break;
}
_ => unreachable!(),
},
}
}
}
});
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(500));
}
loop {
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select_timeout(ms(100));
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
ix if ix == oper1 => {
assert_eq!(oper.recv(&r), Ok(i));
break;
}
_ => unreachable!(),
},
}
}
}
});
})
.unwrap();
}
#[test]
fn send_recv_same_channel() {
let (s, r) = bounded::<i32>(0);
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper2 = sel.recv(&r);
let oper = sel.select_timeout(ms(100));
match oper {
Err(_) => {}
Ok(oper) => match oper.index() {
ix if ix == oper1 => panic!(),
ix if ix == oper2 => panic!(),
_ => unreachable!(),
},
}
let (s, r) = unbounded::<i32>();
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper2 = sel.recv(&r);
let oper = sel.select_timeout(ms(100));
match oper {
Err(_) => panic!(),
Ok(oper) => match oper.index() {
ix if ix == oper1 => assert!(oper.send(&s, 0).is_ok()),
ix if ix == oper2 => panic!(),
_ => unreachable!(),
},
}
}
#[test]
fn matching() {
const THREADS: usize = 44;
let (s, r) = &bounded::<usize>(0);
scope(|scope| {
for i in 0..THREADS {
scope.spawn(move |_| {
let mut sel = Select::new();
let oper1 = sel.recv(r);
let oper2 = sel.send(s);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)),
ix if ix == oper2 => assert!(oper.send(s, i).is_ok()),
_ => unreachable!(),
}
});
}
})
.unwrap();
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn matching_with_leftover() {
const THREADS: usize = 55;
let (s, r) = &bounded::<usize>(0);
scope(|scope| {
for i in 0..THREADS {
scope.spawn(move |_| {
let mut sel = Select::new();
let oper1 = sel.recv(r);
let oper2 = sel.send(s);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)),
ix if ix == oper2 => assert!(oper.send(s, i).is_ok()),
_ => unreachable!(),
}
});
}
s.send(!0).unwrap();
})
.unwrap();
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn channel_through_channel() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
type T = Box<dyn Any + Send>;
for cap in 0..3 {
let (s, r) = bounded::<T>(cap);
scope(|scope| {
scope.spawn(move |_| {
let mut s = s;
for _ in 0..COUNT {
let (new_s, new_r) = bounded(cap);
let new_r: T = Box::new(Some(new_r));
{
let mut sel = Select::new();
let oper1 = sel.send(&s);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert!(oper.send(&s, new_r).is_ok()),
_ => unreachable!(),
}
}
s = new_s;
}
});
scope.spawn(move |_| {
let mut r = r;
for _ in 0..COUNT {
let new = {
let mut sel = Select::new();
let oper1 = sel.recv(&r);
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => oper
.recv(&r)
.unwrap()
.downcast_mut::<Option<Receiver<T>>>()
.unwrap()
.take()
.unwrap(),
_ => unreachable!(),
}
};
r = new;
}
});
})
.unwrap();
}
}
#[test]
fn linearizable_try() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
for step in 0..2 {
let (start_s, start_r) = bounded::<()>(0);
let (end_s, end_r) = bounded::<()>(0);
let ((s1, r1), (s2, r2)) = if step == 0 {
(bounded::<i32>(1), bounded::<i32>(1))
} else {
(unbounded::<i32>(), unbounded::<i32>())
};
scope(|scope| {
scope.spawn(|_| {
for _ in 0..COUNT {
start_s.send(()).unwrap();
s1.send(1).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.try_select();
match oper {
Err(_) => unreachable!(),
Ok(oper) => match oper.index() {
ix if ix == oper1 => assert!(oper.recv(&r1).is_ok()),
ix if ix == oper2 => assert!(oper.recv(&r2).is_ok()),
_ => unreachable!(),
},
}
end_s.send(()).unwrap();
let _ = r2.try_recv();
}
});
for _ in 0..COUNT {
start_r.recv().unwrap();
s2.send(1).unwrap();
let _ = r1.try_recv();
end_r.recv().unwrap();
}
})
.unwrap();
}
}
#[test]
fn linearizable_timeout() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
for step in 0..2 {
let (start_s, start_r) = bounded::<()>(0);
let (end_s, end_r) = bounded::<()>(0);
let ((s1, r1), (s2, r2)) = if step == 0 {
(bounded::<i32>(1), bounded::<i32>(1))
} else {
(unbounded::<i32>(), unbounded::<i32>())
};
scope(|scope| {
scope.spawn(|_| {
for _ in 0..COUNT {
start_s.send(()).unwrap();
s1.send(1).unwrap();
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper = sel.select_timeout(ms(0));
match oper {
Err(_) => unreachable!(),
Ok(oper) => match oper.index() {
ix if ix == oper1 => assert!(oper.recv(&r1).is_ok()),
ix if ix == oper2 => assert!(oper.recv(&r2).is_ok()),
_ => unreachable!(),
},
}
end_s.send(()).unwrap();
let _ = r2.try_recv();
}
});
for _ in 0..COUNT {
start_r.recv().unwrap();
s2.send(1).unwrap();
let _ = r1.try_recv();
end_r.recv().unwrap();
}
})
.unwrap();
}
}
#[test]
fn fairness1() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded::<()>(COUNT);
let (s2, r2) = unbounded::<()>();
for _ in 0..COUNT {
s1.send(()).unwrap();
s2.send(()).unwrap();
}
let hits = vec![Cell::new(0usize); 4];
for _ in 0..COUNT {
let after = after(ms(0));
let tick = tick(ms(0));
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper3 = sel.recv(&after);
let oper4 = sel.recv(&tick);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
oper.recv(&r1).unwrap();
hits[0].set(hits[0].get() + 1);
}
i if i == oper2 => {
oper.recv(&r2).unwrap();
hits[1].set(hits[1].get() + 1);
}
i if i == oper3 => {
oper.recv(&after).unwrap();
hits[2].set(hits[2].get() + 1);
}
i if i == oper4 => {
oper.recv(&tick).unwrap();
hits[3].set(hits[3].get() + 1);
}
_ => unreachable!(),
}
}
assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 2));
}
#[test]
fn fairness2() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = unbounded::<()>();
let (s2, r2) = bounded::<()>(1);
let (s3, r3) = bounded::<()>(0);
scope(|scope| {
scope.spawn(|_| {
for _ in 0..COUNT {
let mut sel = Select::new();
let mut oper1 = None;
let mut oper2 = None;
if s1.is_empty() {
oper1 = Some(sel.send(&s1));
}
if s2.is_empty() {
oper2 = Some(sel.send(&s2));
}
let oper3 = sel.send(&s3);
let oper = sel.select();
match oper.index() {
i if Some(i) == oper1 => assert!(oper.send(&s1, ()).is_ok()),
i if Some(i) == oper2 => assert!(oper.send(&s2, ()).is_ok()),
i if i == oper3 => assert!(oper.send(&s3, ()).is_ok()),
_ => unreachable!(),
}
}
});
let hits = vec![Cell::new(0usize); 3];
for _ in 0..COUNT {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper3 = sel.recv(&r3);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
oper.recv(&r1).unwrap();
hits[0].set(hits[0].get() + 1);
}
i if i == oper2 => {
oper.recv(&r2).unwrap();
hits[1].set(hits[1].get() + 1);
}
i if i == oper3 => {
oper.recv(&r3).unwrap();
hits[2].set(hits[2].get() + 1);
}
_ => unreachable!(),
}
}
assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 50));
})
.unwrap();
}
#[test]
fn sync_and_clone() {
const THREADS: usize = 20;
let (s, r) = &bounded::<usize>(0);
let mut sel = Select::new();
let oper1 = sel.recv(r);
let oper2 = sel.send(s);
let sel = &sel;
scope(|scope| {
for i in 0..THREADS {
scope.spawn(move |_| {
let mut sel = sel.clone();
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)),
ix if ix == oper2 => assert!(oper.send(s, i).is_ok()),
_ => unreachable!(),
}
});
}
})
.unwrap();
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn send_and_clone() {
const THREADS: usize = 20;
let (s, r) = &bounded::<usize>(0);
let mut sel = Select::new();
let oper1 = sel.recv(r);
let oper2 = sel.send(s);
scope(|scope| {
for i in 0..THREADS {
let mut sel = sel.clone();
scope.spawn(move |_| {
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_ne!(oper.recv(r), Ok(i)),
ix if ix == oper2 => assert!(oper.send(s, i).is_ok()),
_ => unreachable!(),
}
});
}
})
.unwrap();
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn reuse() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded(0);
let (s2, r2) = bounded(0);
let (s3, r3) = bounded(100);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
s1.send(i).unwrap();
assert_eq!(r2.recv().unwrap(), i);
r3.recv().unwrap();
}
});
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.send(&s2);
for i in 0..COUNT {
for _ in 0..2 {
let oper = sel.select();
match oper.index() {
ix if ix == oper1 => assert_eq!(oper.recv(&r1), Ok(i)),
ix if ix == oper2 => assert!(oper.send(&s2, i).is_ok()),
_ => unreachable!(),
}
}
s3.send(()).unwrap();
}
})
.unwrap();
}