更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1,336 @@
//! Tests for the after channel flavor.
#![cfg(not(miri))] // TODO: many assertions failed due to Miri is slow
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::{Duration, Instant};
use crossbeam_channel::{after, select, Select, TryRecvError};
use crossbeam_utils::thread::scope;
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn fire() {
let start = Instant::now();
let r = after(ms(50));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
thread::sleep(ms(100));
let fired = r.try_recv().unwrap();
assert!(start < fired);
assert!(fired - start >= ms(50));
let now = Instant::now();
assert!(fired < now);
assert!(now - fired >= ms(50));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
select! {
recv(r) -> _ => panic!(),
default => {}
}
select! {
recv(r) -> _ => panic!(),
recv(after(ms(200))) -> _ => {}
}
}
#[test]
fn capacity() {
const COUNT: usize = 10;
for i in 0..COUNT {
let r = after(ms(i as u64));
assert_eq!(r.capacity(), Some(1));
}
}
#[test]
fn len_empty_full() {
let r = after(ms(50));
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
thread::sleep(ms(100));
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
assert!(r.is_full());
r.try_recv().unwrap();
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
}
#[test]
fn try_recv() {
let r = after(ms(200));
assert!(r.try_recv().is_err());
thread::sleep(ms(100));
assert!(r.try_recv().is_err());
thread::sleep(ms(200));
assert!(r.try_recv().is_ok());
assert!(r.try_recv().is_err());
thread::sleep(ms(200));
assert!(r.try_recv().is_err());
}
#[test]
fn recv() {
let start = Instant::now();
let r = after(ms(50));
let fired = r.recv().unwrap();
assert!(start < fired);
assert!(fired - start >= ms(50));
let now = Instant::now();
assert!(fired < now);
assert!(now - fired < fired - start);
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn recv_timeout() {
let start = Instant::now();
let r = after(ms(200));
assert!(r.recv_timeout(ms(100)).is_err());
let now = Instant::now();
assert!(now - start >= ms(100));
assert!(now - start <= ms(150));
let fired = r.recv_timeout(ms(200)).unwrap();
assert!(fired - start >= ms(200));
assert!(fired - start <= ms(250));
assert!(r.recv_timeout(ms(200)).is_err());
let now = Instant::now();
assert!(now - start >= ms(400));
assert!(now - start <= ms(450));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn recv_two() {
let r1 = after(ms(50));
let r2 = after(ms(50));
scope(|scope| {
scope.spawn(|_| {
select! {
recv(r1) -> _ => {}
recv(r2) -> _ => {}
}
});
scope.spawn(|_| {
select! {
recv(r1) -> _ => {}
recv(r2) -> _ => {}
}
});
})
.unwrap();
}
#[test]
fn recv_race() {
select! {
recv(after(ms(50))) -> _ => {}
recv(after(ms(100))) -> _ => panic!(),
}
select! {
recv(after(ms(100))) -> _ => panic!(),
recv(after(ms(50))) -> _ => {}
}
}
#[test]
fn stress_default() {
const COUNT: usize = 10;
for _ in 0..COUNT {
select! {
recv(after(ms(0))) -> _ => {}
default => panic!(),
}
}
for _ in 0..COUNT {
select! {
recv(after(ms(100))) -> _ => panic!(),
default => {}
}
}
}
#[test]
fn select() {
const THREADS: usize = 4;
const COUNT: usize = 1000;
const TIMEOUT_MS: u64 = 100;
let v = (0..COUNT)
.map(|i| after(ms(i as u64 / TIMEOUT_MS / 2)))
.collect::<Vec<_>>();
let hits = AtomicUsize::new(0);
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
let v: Vec<&_> = v.iter().collect();
loop {
let timeout = after(ms(TIMEOUT_MS));
let mut sel = Select::new();
for r in &v {
sel.recv(r);
}
let oper_timeout = sel.recv(&timeout);
let oper = sel.select();
match oper.index() {
i if i == oper_timeout => {
oper.recv(&timeout).unwrap();
break;
}
i => {
oper.recv(v[i]).unwrap();
hits.fetch_add(1, Ordering::SeqCst);
}
}
}
});
}
})
.unwrap();
assert_eq!(hits.load(Ordering::SeqCst), COUNT);
}
#[test]
fn ready() {
const THREADS: usize = 4;
const COUNT: usize = 1000;
const TIMEOUT_MS: u64 = 100;
let v = (0..COUNT)
.map(|i| after(ms(i as u64 / TIMEOUT_MS / 2)))
.collect::<Vec<_>>();
let hits = AtomicUsize::new(0);
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
let v: Vec<&_> = v.iter().collect();
loop {
let timeout = after(ms(TIMEOUT_MS));
let mut sel = Select::new();
for r in &v {
sel.recv(r);
}
let oper_timeout = sel.recv(&timeout);
loop {
let i = sel.ready();
if i == oper_timeout {
timeout.try_recv().unwrap();
return;
} else if v[i].try_recv().is_ok() {
hits.fetch_add(1, Ordering::SeqCst);
break;
}
}
}
});
}
})
.unwrap();
assert_eq!(hits.load(Ordering::SeqCst), COUNT);
}
#[test]
fn stress_clone() {
const RUNS: usize = 1000;
const THREADS: usize = 10;
const COUNT: usize = 50;
for i in 0..RUNS {
let r = after(ms(i as u64));
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
let r = r.clone();
let _ = r.try_recv();
for _ in 0..COUNT {
drop(r.clone());
thread::yield_now();
}
});
}
})
.unwrap();
}
}
#[test]
fn fairness() {
const COUNT: usize = 1000;
for &dur in &[0, 1] {
let mut hits = [0usize; 2];
for _ in 0..COUNT {
select! {
recv(after(ms(dur))) -> _ => hits[0] += 1,
recv(after(ms(dur))) -> _ => hits[1] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
}
#[test]
fn fairness_duplicates() {
const COUNT: usize = 1000;
for &dur in &[0, 1] {
let mut hits = [0usize; 5];
for _ in 0..COUNT {
let r = after(ms(dur));
select! {
recv(r) -> _ => hits[0] += 1,
recv(r) -> _ => hits[1] += 1,
recv(r) -> _ => hits[2] += 1,
recv(r) -> _ => hits[3] += 1,
recv(r) -> _ => hits[4] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
}

View File

@@ -0,0 +1,744 @@
//! Tests for the array channel flavor.
use std::any::Any;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;
use crossbeam_channel::{bounded, select, Receiver};
use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError};
use crossbeam_channel::{SendError, SendTimeoutError, TrySendError};
use crossbeam_utils::thread::scope;
use rand::{thread_rng, Rng};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke() {
let (s, r) = bounded(1);
s.send(7).unwrap();
assert_eq!(r.try_recv(), Ok(7));
s.send(8).unwrap();
assert_eq!(r.recv(), Ok(8));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout));
}
#[test]
fn capacity() {
for i in 1..10 {
let (s, r) = bounded::<()>(i);
assert_eq!(s.capacity(), Some(i));
assert_eq!(r.capacity(), Some(i));
}
}
#[test]
fn len_empty_full() {
let (s, r) = bounded(2);
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
s.send(()).unwrap();
assert_eq!(s.len(), 1);
assert!(!s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
assert!(!r.is_full());
s.send(()).unwrap();
assert_eq!(s.len(), 2);
assert!(!s.is_empty());
assert!(s.is_full());
assert_eq!(r.len(), 2);
assert!(!r.is_empty());
assert!(r.is_full());
r.recv().unwrap();
assert_eq!(s.len(), 1);
assert!(!s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
assert!(!r.is_full());
}
#[test]
fn try_recv() {
let (s, r) = bounded(100);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
thread::sleep(ms(1500));
assert_eq!(r.try_recv(), Ok(7));
thread::sleep(ms(500));
assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn recv() {
let (s, r) = bounded(100);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Ok(7));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(8));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(9));
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
s.send(8).unwrap();
s.send(9).unwrap();
});
})
.unwrap();
}
#[test]
fn recv_timeout() {
let (s, r) = bounded::<i32>(100);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout));
assert_eq!(r.recv_timeout(ms(1000)), Ok(7));
assert_eq!(
r.recv_timeout(ms(1000)),
Err(RecvTimeoutError::Disconnected)
);
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn try_send() {
let (s, r) = bounded(1);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(s.try_send(1), Ok(()));
assert_eq!(s.try_send(2), Err(TrySendError::Full(2)));
thread::sleep(ms(1500));
assert_eq!(s.try_send(3), Ok(()));
thread::sleep(ms(500));
assert_eq!(s.try_send(4), Err(TrySendError::Disconnected(4)));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
assert_eq!(r.try_recv(), Ok(1));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
assert_eq!(r.recv(), Ok(3));
});
})
.unwrap();
}
#[test]
fn send() {
let (s, r) = bounded(1);
scope(|scope| {
scope.spawn(|_| {
s.send(7).unwrap();
thread::sleep(ms(1000));
s.send(8).unwrap();
thread::sleep(ms(1000));
s.send(9).unwrap();
thread::sleep(ms(1000));
s.send(10).unwrap();
});
scope.spawn(|_| {
thread::sleep(ms(1500));
assert_eq!(r.recv(), Ok(7));
assert_eq!(r.recv(), Ok(8));
assert_eq!(r.recv(), Ok(9));
});
})
.unwrap();
}
#[test]
fn send_timeout() {
let (s, r) = bounded(2);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(s.send_timeout(1, ms(1000)), Ok(()));
assert_eq!(s.send_timeout(2, ms(1000)), Ok(()));
assert_eq!(
s.send_timeout(3, ms(500)),
Err(SendTimeoutError::Timeout(3))
);
thread::sleep(ms(1000));
assert_eq!(s.send_timeout(4, ms(1000)), Ok(()));
thread::sleep(ms(1000));
assert_eq!(s.send(5), Err(SendError(5)));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(1));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(2));
assert_eq!(r.recv(), Ok(4));
});
})
.unwrap();
}
#[test]
fn send_after_disconnect() {
let (s, r) = bounded(100);
s.send(1).unwrap();
s.send(2).unwrap();
s.send(3).unwrap();
drop(r);
assert_eq!(s.send(4), Err(SendError(4)));
assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5)));
assert_eq!(
s.send_timeout(6, ms(500)),
Err(SendTimeoutError::Disconnected(6))
);
}
#[test]
fn recv_after_disconnect() {
let (s, r) = bounded(100);
s.send(1).unwrap();
s.send(2).unwrap();
s.send(3).unwrap();
drop(s);
assert_eq!(r.recv(), Ok(1));
assert_eq!(r.recv(), Ok(2));
assert_eq!(r.recv(), Ok(3));
assert_eq!(r.recv(), Err(RecvError));
}
#[test]
fn len() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
#[cfg(miri)]
const CAP: usize = 50;
#[cfg(not(miri))]
const CAP: usize = 1000;
let (s, r) = bounded(CAP);
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
for _ in 0..CAP / 10 {
for i in 0..50 {
s.send(i).unwrap();
assert_eq!(s.len(), i + 1);
}
for i in 0..50 {
r.recv().unwrap();
assert_eq!(r.len(), 50 - i - 1);
}
}
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
for i in 0..CAP {
s.send(i).unwrap();
assert_eq!(s.len(), i + 1);
}
for _ in 0..CAP {
r.recv().unwrap();
}
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
assert_eq!(r.recv(), Ok(i));
let len = r.len();
assert!(len <= CAP);
}
});
scope.spawn(|_| {
for i in 0..COUNT {
s.send(i).unwrap();
let len = s.len();
assert!(len <= CAP);
}
});
})
.unwrap();
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
}
#[test]
fn disconnect_wakes_sender() {
let (s, r) = bounded(1);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(s.send(()), Ok(()));
assert_eq!(s.send(()), Err(SendError(())));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
drop(r);
});
})
.unwrap();
}
#[test]
fn disconnect_wakes_receiver() {
let (s, r) = bounded::<()>(1);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
drop(s);
});
})
.unwrap();
}
#[test]
fn spsc() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
let (s, r) = bounded(3);
scope(|scope| {
scope.spawn(move |_| {
for i in 0..COUNT {
assert_eq!(r.recv(), Ok(i));
}
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
})
.unwrap();
}
#[test]
fn mpmc() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
const THREADS: usize = 4;
let (s, r) = bounded::<usize>(3);
let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::<Vec<_>>();
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
for _ in 0..COUNT {
let n = r.recv().unwrap();
v[n].fetch_add(1, Ordering::SeqCst);
}
});
}
for _ in 0..THREADS {
scope.spawn(|_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
}
})
.unwrap();
for c in v {
assert_eq!(c.load(Ordering::SeqCst), THREADS);
}
}
#[test]
fn stress_oneshot() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
for _ in 0..COUNT {
let (s, r) = bounded(1);
scope(|scope| {
scope.spawn(|_| r.recv().unwrap());
scope.spawn(|_| s.send(0).unwrap());
})
.unwrap();
}
}
#[test]
fn stress_iter() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
let (request_s, request_r) = bounded(1);
let (response_s, response_r) = bounded(1);
scope(|scope| {
scope.spawn(move |_| {
let mut count = 0;
loop {
for x in response_r.try_iter() {
count += x;
if count == COUNT {
return;
}
}
request_s.send(()).unwrap();
}
});
for _ in request_r.iter() {
if response_s.send(1).is_err() {
break;
}
}
})
.unwrap();
}
#[test]
fn stress_timeout_two_threads() {
const COUNT: usize = 100;
let (s, r) = bounded(2);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
loop {
if let Ok(()) = s.send_timeout(i, ms(10)) {
break;
}
}
}
});
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
loop {
if let Ok(x) = r.recv_timeout(ms(10)) {
assert_eq!(x, i);
break;
}
}
}
});
})
.unwrap();
}
#[test]
fn drops() {
#[cfg(miri)]
const RUNS: usize = 10;
#[cfg(not(miri))]
const RUNS: usize = 100;
#[cfg(miri)]
const STEPS: usize = 100;
#[cfg(not(miri))]
const STEPS: usize = 10_000;
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
let mut rng = thread_rng();
for _ in 0..RUNS {
let steps = rng.gen_range(0..STEPS);
let additional = rng.gen_range(0..50);
DROPS.store(0, Ordering::SeqCst);
let (s, r) = bounded::<DropCounter>(50);
scope(|scope| {
scope.spawn(|_| {
for _ in 0..steps {
r.recv().unwrap();
}
});
scope.spawn(|_| {
for _ in 0..steps {
s.send(DropCounter).unwrap();
}
});
})
.unwrap();
for _ in 0..additional {
s.send(DropCounter).unwrap();
}
assert_eq!(DROPS.load(Ordering::SeqCst), steps);
drop(s);
drop(r);
assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional);
}
}
#[test]
fn linearizable() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
const THREADS: usize = 4;
let (s, r) = bounded(THREADS);
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
for _ in 0..COUNT {
s.send(0).unwrap();
r.try_recv().unwrap();
}
});
}
})
.unwrap();
}
#[test]
fn fairness() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded::<()>(COUNT);
let (s2, r2) = bounded::<()>(COUNT);
for _ in 0..COUNT {
s1.send(()).unwrap();
s2.send(()).unwrap();
}
let mut hits = [0usize; 2];
for _ in 0..COUNT {
select! {
recv(r1) -> _ => hits[0] += 1,
recv(r2) -> _ => hits[1] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
#[test]
fn fairness_duplicates() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s, r) = bounded::<()>(COUNT);
for _ in 0..COUNT {
s.send(()).unwrap();
}
let mut hits = [0usize; 5];
for _ in 0..COUNT {
select! {
recv(r) -> _ => hits[0] += 1,
recv(r) -> _ => hits[1] += 1,
recv(r) -> _ => hits[2] += 1,
recv(r) -> _ => hits[3] += 1,
recv(r) -> _ => hits[4] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
#[test]
fn recv_in_send() {
let (s, _r) = bounded(1);
s.send(()).unwrap();
#[allow(unreachable_code)]
{
select! {
send(s, panic!()) -> _ => panic!(),
default => {}
}
}
let (s, r) = bounded(2);
s.send(()).unwrap();
select! {
send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {}
}
}
#[test]
fn channel_through_channel() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 1000;
type T = Box<dyn Any + Send>;
let (s, r) = bounded::<T>(1);
scope(|scope| {
scope.spawn(move |_| {
let mut s = s;
for _ in 0..COUNT {
let (new_s, new_r) = bounded(1);
let new_r: T = Box::new(Some(new_r));
s.send(new_r).unwrap();
s = new_s;
}
});
scope.spawn(move |_| {
let mut r = r;
for _ in 0..COUNT {
r = r
.recv()
.unwrap()
.downcast_mut::<Option<Receiver<T>>>()
.unwrap()
.take()
.unwrap()
}
});
})
.unwrap();
}
#[test]
fn panic_on_drop() {
struct Msg1<'a>(&'a mut bool);
impl Drop for Msg1<'_> {
fn drop(&mut self) {
if *self.0 && !std::thread::panicking() {
panic!("double drop");
} else {
*self.0 = true;
}
}
}
struct Msg2<'a>(&'a mut bool);
impl Drop for Msg2<'_> {
fn drop(&mut self) {
if *self.0 {
panic!("double drop");
} else {
*self.0 = true;
panic!("first drop");
}
}
}
// normal
let (s, r) = bounded(2);
let (mut a, mut b) = (false, false);
s.send(Msg1(&mut a)).unwrap();
s.send(Msg1(&mut b)).unwrap();
drop(s);
drop(r);
assert!(a);
assert!(b);
// panic on drop
let (s, r) = bounded(2);
let (mut a, mut b) = (false, false);
s.send(Msg2(&mut a)).unwrap();
s.send(Msg2(&mut b)).unwrap();
drop(s);
let res = std::panic::catch_unwind(move || {
drop(r);
});
assert_eq!(
*res.unwrap_err().downcast_ref::<&str>().unwrap(),
"first drop"
);
assert!(a);
// Elements after the panicked element will leak.
assert!(!b);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
//! Tests for iteration over receivers.
use crossbeam_channel::unbounded;
use crossbeam_utils::thread::scope;
#[test]
fn nested_recv_iter() {
let (s, r) = unbounded::<i32>();
let (total_s, total_r) = unbounded::<i32>();
scope(|scope| {
scope.spawn(move |_| {
let mut acc = 0;
for x in r.iter() {
acc += x;
}
total_s.send(acc).unwrap();
});
s.send(3).unwrap();
s.send(1).unwrap();
s.send(2).unwrap();
drop(s);
assert_eq!(total_r.recv().unwrap(), 6);
})
.unwrap();
}
#[test]
fn recv_iter_break() {
let (s, r) = unbounded::<i32>();
let (count_s, count_r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
let mut count = 0;
for x in r.iter() {
if count >= 3 {
break;
} else {
count += x;
}
}
count_s.send(count).unwrap();
});
s.send(2).unwrap();
s.send(2).unwrap();
s.send(2).unwrap();
let _ = s.send(2);
drop(s);
assert_eq!(count_r.recv().unwrap(), 4);
})
.unwrap();
}
#[test]
fn recv_try_iter() {
let (request_s, request_r) = unbounded();
let (response_s, response_r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
let mut count = 0;
loop {
for x in response_r.try_iter() {
count += x;
if count == 6 {
return;
}
}
request_s.send(()).unwrap();
}
});
for _ in request_r.iter() {
if response_s.send(2).is_err() {
break;
}
}
})
.unwrap();
}
#[test]
fn recv_into_iter_owned() {
let mut iter = {
let (s, r) = unbounded::<i32>();
s.send(1).unwrap();
s.send(2).unwrap();
r.into_iter()
};
assert_eq!(iter.next().unwrap(), 1);
assert_eq!(iter.next().unwrap(), 2);
assert!(iter.next().is_none());
}
#[test]
fn recv_into_iter_borrowed() {
let (s, r) = unbounded::<i32>();
s.send(1).unwrap();
s.send(2).unwrap();
drop(s);
let mut iter = (&r).into_iter();
assert_eq!(iter.next().unwrap(), 1);
assert_eq!(iter.next().unwrap(), 2);
assert!(iter.next().is_none());
}

View File

@@ -0,0 +1,582 @@
//! Tests for the list channel flavor.
use std::any::Any;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;
use crossbeam_channel::{select, unbounded, Receiver};
use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError};
use crossbeam_channel::{SendError, SendTimeoutError, TrySendError};
use crossbeam_utils::thread::scope;
use rand::{thread_rng, Rng};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke() {
let (s, r) = unbounded();
s.try_send(7).unwrap();
assert_eq!(r.try_recv(), Ok(7));
s.send(8).unwrap();
assert_eq!(r.recv(), Ok(8));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout));
}
#[test]
fn capacity() {
let (s, r) = unbounded::<()>();
assert_eq!(s.capacity(), None);
assert_eq!(r.capacity(), None);
}
#[test]
fn len_empty_full() {
let (s, r) = unbounded();
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
s.send(()).unwrap();
assert_eq!(s.len(), 1);
assert!(!s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
assert!(!r.is_full());
r.recv().unwrap();
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert!(!s.is_full());
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
}
#[test]
#[cfg_attr(miri, ignore)] // this test makes timing assumptions, but Miri is so slow it violates them
fn try_recv() {
let (s, r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
thread::sleep(ms(1500));
assert_eq!(r.try_recv(), Ok(7));
thread::sleep(ms(500));
assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn recv() {
let (s, r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Ok(7));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(8));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(9));
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
s.send(8).unwrap();
s.send(9).unwrap();
});
})
.unwrap();
}
#[test]
fn recv_timeout() {
let (s, r) = unbounded::<i32>();
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout));
assert_eq!(r.recv_timeout(ms(1000)), Ok(7));
assert_eq!(
r.recv_timeout(ms(1000)),
Err(RecvTimeoutError::Disconnected)
);
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn try_send() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
let (s, r) = unbounded();
for i in 0..COUNT {
assert_eq!(s.try_send(i), Ok(()));
}
drop(r);
assert_eq!(s.try_send(777), Err(TrySendError::Disconnected(777)));
}
#[test]
fn send() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
let (s, r) = unbounded();
for i in 0..COUNT {
assert_eq!(s.send(i), Ok(()));
}
drop(r);
assert_eq!(s.send(777), Err(SendError(777)));
}
#[test]
fn send_timeout() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
let (s, r) = unbounded();
for i in 0..COUNT {
assert_eq!(s.send_timeout(i, ms(i as u64)), Ok(()));
}
drop(r);
assert_eq!(
s.send_timeout(777, ms(0)),
Err(SendTimeoutError::Disconnected(777))
);
}
#[test]
fn send_after_disconnect() {
let (s, r) = unbounded();
s.send(1).unwrap();
s.send(2).unwrap();
s.send(3).unwrap();
drop(r);
assert_eq!(s.send(4), Err(SendError(4)));
assert_eq!(s.try_send(5), Err(TrySendError::Disconnected(5)));
assert_eq!(
s.send_timeout(6, ms(0)),
Err(SendTimeoutError::Disconnected(6))
);
}
#[test]
fn recv_after_disconnect() {
let (s, r) = unbounded();
s.send(1).unwrap();
s.send(2).unwrap();
s.send(3).unwrap();
drop(s);
assert_eq!(r.recv(), Ok(1));
assert_eq!(r.recv(), Ok(2));
assert_eq!(r.recv(), Ok(3));
assert_eq!(r.recv(), Err(RecvError));
}
#[test]
fn len() {
let (s, r) = unbounded();
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
for i in 0..50 {
s.send(i).unwrap();
assert_eq!(s.len(), i + 1);
}
for i in 0..50 {
r.recv().unwrap();
assert_eq!(r.len(), 50 - i - 1);
}
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
}
#[test]
fn disconnect_wakes_receiver() {
let (s, r) = unbounded::<()>();
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
drop(s);
});
})
.unwrap();
}
#[test]
fn spsc() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
let (s, r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
for i in 0..COUNT {
assert_eq!(r.recv(), Ok(i));
}
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
})
.unwrap();
}
#[test]
fn mpmc() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
const THREADS: usize = 4;
let (s, r) = unbounded::<usize>();
let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::<Vec<_>>();
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
for _ in 0..COUNT {
let n = r.recv().unwrap();
v[n].fetch_add(1, Ordering::SeqCst);
}
});
}
for _ in 0..THREADS {
scope.spawn(|_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
}
})
.unwrap();
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
for c in v {
assert_eq!(c.load(Ordering::SeqCst), THREADS);
}
}
#[test]
fn stress_oneshot() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
for _ in 0..COUNT {
let (s, r) = unbounded();
scope(|scope| {
scope.spawn(|_| r.recv().unwrap());
scope.spawn(|_| s.send(0).unwrap());
})
.unwrap();
}
}
#[test]
fn stress_iter() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
let (request_s, request_r) = unbounded();
let (response_s, response_r) = unbounded();
scope(|scope| {
scope.spawn(move |_| {
let mut count = 0;
loop {
for x in response_r.try_iter() {
count += x;
if count == COUNT {
return;
}
}
request_s.send(()).unwrap();
}
});
for _ in request_r.iter() {
if response_s.send(1).is_err() {
break;
}
}
})
.unwrap();
}
#[test]
fn stress_timeout_two_threads() {
const COUNT: usize = 100;
let (s, r) = unbounded();
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
s.send(i).unwrap();
}
});
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
loop {
if let Ok(x) = r.recv_timeout(ms(10)) {
assert_eq!(x, i);
break;
}
}
}
});
})
.unwrap();
}
#[test]
fn drops() {
#[cfg(miri)]
const RUNS: usize = 20;
#[cfg(not(miri))]
const RUNS: usize = 100;
#[cfg(miri)]
const STEPS: usize = 100;
#[cfg(not(miri))]
const STEPS: usize = 10_000;
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
let mut rng = thread_rng();
for _ in 0..RUNS {
let steps = rng.gen_range(0..STEPS);
let additional = rng.gen_range(0..STEPS / 10);
DROPS.store(0, Ordering::SeqCst);
let (s, r) = unbounded::<DropCounter>();
scope(|scope| {
scope.spawn(|_| {
for _ in 0..steps {
r.recv().unwrap();
}
});
scope.spawn(|_| {
for _ in 0..steps {
s.send(DropCounter).unwrap();
}
});
})
.unwrap();
for _ in 0..additional {
s.try_send(DropCounter).unwrap();
}
assert_eq!(DROPS.load(Ordering::SeqCst), steps);
drop(s);
drop(r);
assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional);
}
}
#[test]
fn linearizable() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
const THREADS: usize = 4;
let (s, r) = unbounded();
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
for _ in 0..COUNT {
s.send(0).unwrap();
r.try_recv().unwrap();
}
});
}
})
.unwrap();
}
#[test]
fn fairness() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = unbounded::<()>();
let (s2, r2) = unbounded::<()>();
for _ in 0..COUNT {
s1.send(()).unwrap();
s2.send(()).unwrap();
}
let mut hits = [0usize; 2];
for _ in 0..COUNT {
select! {
recv(r1) -> _ => hits[0] += 1,
recv(r2) -> _ => hits[1] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
#[test]
fn fairness_duplicates() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s, r) = unbounded();
for _ in 0..COUNT {
s.send(()).unwrap();
}
let mut hits = [0usize; 5];
for _ in 0..COUNT {
select! {
recv(r) -> _ => hits[0] += 1,
recv(r) -> _ => hits[1] += 1,
recv(r) -> _ => hits[2] += 1,
recv(r) -> _ => hits[3] += 1,
recv(r) -> _ => hits[4] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
#[test]
fn recv_in_send() {
let (s, r) = unbounded();
s.send(()).unwrap();
select! {
send(s, assert_eq!(r.recv(), Ok(()))) -> _ => {}
}
}
#[test]
fn channel_through_channel() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 1000;
type T = Box<dyn Any + Send>;
let (s, r) = unbounded::<T>();
scope(|scope| {
scope.spawn(move |_| {
let mut s = s;
for _ in 0..COUNT {
let (new_s, new_r) = unbounded();
let new_r: T = Box::new(Some(new_r));
s.send(new_r).unwrap();
s = new_s;
}
});
scope.spawn(move |_| {
let mut r = r;
for _ in 0..COUNT {
r = r
.recv()
.unwrap()
.downcast_mut::<Option<Receiver<T>>>()
.unwrap()
.take()
.unwrap()
}
});
})
.unwrap();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
//! Tests for the never channel flavor.
use std::thread;
use std::time::{Duration, Instant};
use crossbeam_channel::{never, select, tick, unbounded};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke() {
select! {
recv(never::<i32>()) -> _ => panic!(),
default => {}
}
}
#[test]
fn optional() {
let (s, r) = unbounded::<i32>();
s.send(1).unwrap();
s.send(2).unwrap();
let mut r = Some(&r);
select! {
recv(r.unwrap_or(&never())) -> _ => {}
default => panic!(),
}
r = None;
select! {
recv(r.unwrap_or(&never())) -> _ => panic!(),
default => {}
}
}
#[test]
fn tick_n() {
let mut r = tick(ms(100));
let mut step = 0;
loop {
select! {
recv(r) -> _ => step += 1,
default(ms(500)) => break,
}
if step == 10 {
r = never();
}
}
assert_eq!(step, 10);
}
#[test]
fn capacity() {
let r = never::<i32>();
assert_eq!(r.capacity(), Some(0));
}
#[test]
fn len_empty_full() {
let r = never::<i32>();
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(r.is_full());
}
#[test]
fn try_recv() {
let r = never::<i32>();
assert!(r.try_recv().is_err());
thread::sleep(ms(100));
assert!(r.try_recv().is_err());
}
#[test]
fn recv_timeout() {
let start = Instant::now();
let r = never::<i32>();
assert!(r.recv_timeout(ms(100)).is_err());
let now = Instant::now();
assert!(now - start >= ms(100));
assert!(now - start <= ms(150));
assert!(r.recv_timeout(ms(100)).is_err());
let now = Instant::now();
assert!(now - start >= ms(200));
assert!(now - start <= ms(250));
}

View File

@@ -0,0 +1,852 @@
//! Tests for channel readiness 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};
use crossbeam_channel::{Receiver, Select, TryRecvError, TrySendError};
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();
sel.recv(&r1);
sel.recv(&r2);
assert_eq!(sel.ready(), 0);
assert_eq!(r1.try_recv(), Ok(1));
s2.send(2).unwrap();
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
assert_eq!(sel.ready(), 1);
assert_eq!(r2.try_recv(), Ok(2));
}
#[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();
sel.recv(&r1);
sel.recv(&r2);
sel.recv(&r3);
sel.recv(&r4);
sel.recv(&r5);
assert_eq!(sel.ready(), 4);
assert_eq!(r5.try_recv(), Ok(5));
}
#[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();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready_timeout(ms(1000)) {
Ok(0) => assert_eq!(r1.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
r2.recv().unwrap();
})
.unwrap();
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready_timeout(ms(1000)) {
Ok(0) => assert_eq!(r1.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(500));
drop(s2);
});
let mut sel = Select::new();
sel.recv(&r2);
match sel.ready_timeout(ms(1000)) {
Ok(0) => assert_eq!(r2.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
})
.unwrap();
}
#[test]
fn default() {
let (s1, r1) = unbounded::<i32>();
let (s2, r2) = unbounded::<i32>();
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
assert!(sel.try_ready().is_err());
drop(s1);
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
match sel.try_ready() {
Ok(0) => assert!(r1.try_recv().is_err()),
_ => panic!(),
}
s2.send(2).unwrap();
let mut sel = Select::new();
sel.recv(&r2);
match sel.try_ready() {
Ok(0) => assert_eq!(r2.try_recv(), Ok(2)),
_ => panic!(),
}
let mut sel = Select::new();
sel.recv(&r2);
assert!(sel.try_ready().is_err());
let mut sel = Select::new();
assert!(sel.try_ready().is_err());
}
#[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();
sel.recv(&r1);
sel.recv(&r2);
assert!(sel.ready_timeout(ms(1000)).is_err());
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready_timeout(ms(1000)) {
Ok(1) => assert_eq!(r2.try_recv(), Ok(2)),
_ => panic!(),
}
})
.unwrap();
scope(|scope| {
let (s, r) = unbounded::<i32>();
scope.spawn(move |_| {
thread::sleep(ms(500));
drop(s);
});
let mut sel = Select::new();
assert!(sel.ready_timeout(ms(1000)).is_err());
let mut sel = Select::new();
sel.recv(&r);
match sel.try_ready() {
Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
})
.unwrap();
}
#[test]
fn default_when_disconnected() {
let (_, r) = unbounded::<i32>();
let mut sel = Select::new();
sel.recv(&r);
match sel.try_ready() {
Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
let (_, r) = unbounded::<i32>();
let mut sel = Select::new();
sel.recv(&r);
match sel.ready_timeout(ms(1000)) {
Ok(0) => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
let (s, _) = bounded::<i32>(0);
let mut sel = Select::new();
sel.send(&s);
match sel.try_ready() {
Ok(0) => assert_eq!(s.try_send(0), Err(TrySendError::Disconnected(0))),
_ => panic!(),
}
let (s, _) = bounded::<i32>(0);
let mut sel = Select::new();
sel.send(&s);
match sel.ready_timeout(ms(1000)) {
Ok(0) => assert_eq!(s.try_send(0), Err(TrySendError::Disconnected(0))),
_ => panic!(),
}
}
#[test]
fn default_only() {
let start = Instant::now();
let mut sel = Select::new();
assert!(sel.try_ready().is_err());
let now = Instant::now();
assert!(now - start <= ms(50));
let start = Instant::now();
let mut sel = Select::new();
assert!(sel.ready_timeout(ms(500)).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();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready_timeout(ms(1000)) {
Ok(1) => assert_eq!(r2.try_recv(), Ok(2)),
_ => panic!(),
}
})
.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();
sel.recv(&r1);
sel.send(&s2);
match sel.ready() {
0 => assert_eq!(r1.try_recv(), Ok(1)),
1 => s2.try_send(2).unwrap(),
_ => panic!(),
}
}
})
.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();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready() {
0 => drop(r1.try_recv()),
1 => drop(r2.try_recv()),
_ => panic!(),
}
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();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready() {
0 => panic!(),
1 => drop(r2.try_recv()),
_ => panic!(),
}
});
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();
sel.recv(&r);
match sel.ready() {
0 => drop(r.try_recv()),
_ => panic!(),
}
}
#[test]
fn preflight2() {
let (s, r) = unbounded();
drop(s.clone());
s.send(()).unwrap();
drop(s);
let mut sel = Select::new();
sel.recv(&r);
match sel.ready() {
0 => assert_eq!(r.try_recv(), Ok(())),
_ => panic!(),
}
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();
sel.recv(&r);
match sel.ready() {
0 => assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected)),
_ => panic!(),
}
}
#[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();
sel.recv(&r);
sel.recv(&r);
sel.send(&s);
sel.send(&s);
match sel.ready() {
0 => {
assert!(r.try_recv().is_ok());
hit[0].set(true);
}
1 => {
assert!(r.try_recv().is_ok());
hit[1].set(true);
}
2 => {
assert!(s.try_send(0).is_ok());
hit[2].set(true);
}
3 => {
assert!(s.try_send(0).is_ok());
hit[3].set(true);
}
_ => panic!(),
}
}
}
#[test]
fn nesting() {
let (s, r) = unbounded::<i32>();
let mut sel = Select::new();
sel.send(&s);
match sel.ready() {
0 => {
assert!(s.try_send(0).is_ok());
let mut sel = Select::new();
sel.recv(&r);
match sel.ready() {
0 => {
assert_eq!(r.try_recv(), Ok(0));
let mut sel = Select::new();
sel.send(&s);
match sel.ready() {
0 => {
assert!(s.try_send(1).is_ok());
let mut sel = Select::new();
sel.recv(&r);
match sel.ready() {
0 => {
assert_eq!(r.try_recv(), Ok(1));
}
_ => panic!(),
}
}
_ => panic!(),
}
}
_ => panic!(),
}
}
_ => panic!(),
}
}
#[test]
fn stress_recv() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = unbounded();
let (s2, r2) = bounded(5);
let (s3, r3) = bounded(0);
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();
sel.recv(&r1);
sel.recv(&r2);
match sel.ready() {
0 => assert_eq!(r1.try_recv(), Ok(i)),
1 => assert_eq!(r2.try_recv(), Ok(i)),
_ => panic!(),
}
s3.send(()).unwrap();
}
}
})
.unwrap();
}
#[test]
fn stress_send() {
#[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 {
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();
sel.send(&s1);
sel.send(&s2);
match sel.ready() {
0 => assert!(s1.try_send(i).is_ok()),
1 => assert!(s2.try_send(i).is_ok()),
_ => panic!(),
}
}
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();
sel.recv(&r1);
sel.send(&s2);
match sel.ready() {
0 => assert_eq!(r1.try_recv(), Ok(i)),
1 => assert!(s2.try_send(i).is_ok()),
_ => panic!(),
}
}
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();
sel.send(&s);
match sel.ready_timeout(ms(100)) {
Err(_) => {}
Ok(0) => {
assert!(s.try_send(i).is_ok());
break;
}
Ok(_) => panic!(),
}
}
}
});
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(500));
}
loop {
let mut sel = Select::new();
sel.recv(&r);
match sel.ready_timeout(ms(100)) {
Err(_) => {}
Ok(0) => {
assert_eq!(r.try_recv(), Ok(i));
break;
}
Ok(_) => panic!(),
}
}
}
});
})
.unwrap();
}
#[test]
fn send_recv_same_channel() {
let (s, r) = bounded::<i32>(0);
let mut sel = Select::new();
sel.send(&s);
sel.recv(&r);
assert!(sel.ready_timeout(ms(100)).is_err());
let (s, r) = unbounded::<i32>();
let mut sel = Select::new();
sel.send(&s);
sel.recv(&r);
match sel.ready_timeout(ms(100)) {
Err(_) => panic!(),
Ok(0) => assert!(s.try_send(0).is_ok()),
Ok(_) => panic!(),
}
}
#[test]
fn channel_through_channel() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 1000;
type T = Box<dyn Any + Send>;
for cap in 1..4 {
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();
sel.send(&s);
match sel.ready() {
0 => assert!(s.try_send(new_r).is_ok()),
_ => panic!(),
}
}
s = new_s;
}
});
scope.spawn(move |_| {
let mut r = r;
for _ in 0..COUNT {
let new = {
let mut sel = Select::new();
sel.recv(&r);
match sel.ready() {
0 => r
.try_recv()
.unwrap()
.downcast_mut::<Option<Receiver<T>>>()
.unwrap()
.take()
.unwrap(),
_ => panic!(),
}
};
r = new;
}
});
})
.unwrap();
}
}
#[test]
fn fairness1() {
#[cfg(miri)]
const COUNT: usize = 100;
#[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();
sel.recv(&r1);
sel.recv(&r2);
sel.recv(&after);
sel.recv(&tick);
match sel.ready() {
0 => {
r1.try_recv().unwrap();
hits[0].set(hits[0].get() + 1);
}
1 => {
r2.try_recv().unwrap();
hits[1].set(hits[1].get() + 1);
}
2 => {
after.try_recv().unwrap();
hits[2].set(hits[2].get() + 1);
}
3 => {
tick.try_recv().unwrap();
hits[3].set(hits[3].get() + 1);
}
_ => panic!(),
}
}
assert!(hits.iter().all(|x| x.get() >= COUNT / hits.len() / 2));
}
#[test]
fn fairness2() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 100_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();
sel.recv(&r1);
sel.recv(&r2);
sel.recv(&r3);
loop {
match sel.ready() {
0 => {
if r1.try_recv().is_ok() {
hits[0].set(hits[0].get() + 1);
break;
}
}
1 => {
if r2.try_recv().is_ok() {
hits[1].set(hits[1].get() + 1);
break;
}
}
2 => {
if r3.try_recv().is_ok() {
hits[2].set(hits[2].get() + 1);
break;
}
}
_ => unreachable!(),
}
}
}
assert!(hits.iter().all(|x| x.get() > 0));
})
.unwrap();
}

View File

@@ -0,0 +1,112 @@
use std::time::Duration;
use crossbeam_channel::{after, bounded, never, tick, unbounded};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn after_same_channel() {
let r = after(ms(50));
let r2 = r.clone();
assert!(r.same_channel(&r2));
let r3 = after(ms(50));
assert!(!r.same_channel(&r3));
assert!(!r2.same_channel(&r3));
let r4 = after(ms(100));
assert!(!r.same_channel(&r4));
assert!(!r2.same_channel(&r4));
}
#[test]
fn array_same_channel() {
let (s, r) = bounded::<usize>(1);
let s2 = s.clone();
assert!(s.same_channel(&s2));
let r2 = r.clone();
assert!(r.same_channel(&r2));
let (s3, r3) = bounded::<usize>(1);
assert!(!s.same_channel(&s3));
assert!(!s2.same_channel(&s3));
assert!(!r.same_channel(&r3));
assert!(!r2.same_channel(&r3));
}
#[test]
fn list_same_channel() {
let (s, r) = unbounded::<usize>();
let s2 = s.clone();
assert!(s.same_channel(&s2));
let r2 = r.clone();
assert!(r.same_channel(&r2));
let (s3, r3) = unbounded::<usize>();
assert!(!s.same_channel(&s3));
assert!(!s2.same_channel(&s3));
assert!(!r.same_channel(&r3));
assert!(!r2.same_channel(&r3));
}
#[test]
fn never_same_channel() {
let r = never::<usize>();
let r2 = r.clone();
assert!(r.same_channel(&r2));
// Never channel are always equal to one another.
let r3 = never::<usize>();
assert!(r.same_channel(&r3));
assert!(r2.same_channel(&r3));
}
#[test]
fn tick_same_channel() {
let r = tick(ms(50));
let r2 = r.clone();
assert!(r.same_channel(&r2));
let r3 = tick(ms(50));
assert!(!r.same_channel(&r3));
assert!(!r2.same_channel(&r3));
let r4 = tick(ms(100));
assert!(!r.same_channel(&r4));
assert!(!r2.same_channel(&r4));
}
#[test]
fn zero_same_channel() {
let (s, r) = bounded::<usize>(0);
let s2 = s.clone();
assert!(s.same_channel(&s2));
let r2 = r.clone();
assert!(r.same_channel(&r2));
let (s3, r3) = bounded::<usize>(0);
assert!(!s.same_channel(&s3));
assert!(!s2.same_channel(&s3));
assert!(!r.same_channel(&r3));
assert!(!r2.same_channel(&r3));
}
#[test]
fn different_flavors_same_channel() {
let (s1, r1) = bounded::<usize>(0);
let (s2, r2) = unbounded::<usize>();
assert!(!s1.same_channel(&s2));
assert!(!r1.same_channel(&r2));
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
//! Tests that make sure accessing thread-locals while exiting the thread doesn't cause panics.
#![cfg(not(miri))] // Miri detects that this test is buggy: the destructor of `FOO` uses `std::thread::current()`!
use std::thread;
use std::time::Duration;
use crossbeam_channel::{select, unbounded};
use crossbeam_utils::thread::scope;
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
#[cfg_attr(target_os = "macos", ignore = "TLS is destroyed too early on macOS")]
fn use_while_exiting() {
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
// A blocking operation after the thread-locals have been dropped. This will attempt to
// use the thread-locals and must not panic.
let (_s, r) = unbounded::<()>();
select! {
recv(r) -> _ => {}
default(ms(100)) => {}
}
}
}
thread_local! {
static FOO: Foo = Foo;
}
let (s, r) = unbounded::<()>();
scope(|scope| {
scope.spawn(|_| {
// First initialize `FOO`, then the thread-locals related to crossbeam-channel.
FOO.with(|_| ());
r.recv().unwrap();
// At thread exit, thread-locals related to crossbeam-channel get dropped first and
// `FOO` is dropped last.
});
scope.spawn(|_| {
thread::sleep(ms(100));
s.send(()).unwrap();
});
})
.unwrap();
}

View File

@@ -0,0 +1,352 @@
//! Tests for the tick channel flavor.
#![cfg(not(miri))] // TODO: many assertions failed due to Miri is slow
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::{Duration, Instant};
use crossbeam_channel::{after, select, tick, Select, TryRecvError};
use crossbeam_utils::thread::scope;
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn fire() {
let start = Instant::now();
let r = tick(ms(50));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
thread::sleep(ms(100));
let fired = r.try_recv().unwrap();
assert!(start < fired);
assert!(fired - start >= ms(50));
let now = Instant::now();
assert!(fired < now);
assert!(now - fired >= ms(50));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
select! {
recv(r) -> _ => panic!(),
default => {}
}
select! {
recv(r) -> _ => {}
recv(tick(ms(200))) -> _ => panic!(),
}
}
#[test]
fn intervals() {
let start = Instant::now();
let r = tick(ms(50));
let t1 = r.recv().unwrap();
assert!(start + ms(50) <= t1);
assert!(start + ms(100) > t1);
thread::sleep(ms(300));
let t2 = r.try_recv().unwrap();
assert!(start + ms(100) <= t2);
assert!(start + ms(150) > t2);
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
let t3 = r.recv().unwrap();
assert!(start + ms(400) <= t3);
assert!(start + ms(450) > t3);
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn capacity() {
const COUNT: usize = 10;
for i in 0..COUNT {
let r = tick(ms(i as u64));
assert_eq!(r.capacity(), Some(1));
}
}
#[test]
fn len_empty_full() {
let r = tick(ms(50));
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
thread::sleep(ms(100));
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
assert!(r.is_full());
r.try_recv().unwrap();
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(!r.is_full());
}
#[test]
fn try_recv() {
let r = tick(ms(200));
assert!(r.try_recv().is_err());
thread::sleep(ms(100));
assert!(r.try_recv().is_err());
thread::sleep(ms(200));
assert!(r.try_recv().is_ok());
assert!(r.try_recv().is_err());
thread::sleep(ms(200));
assert!(r.try_recv().is_ok());
assert!(r.try_recv().is_err());
}
#[test]
fn recv() {
let start = Instant::now();
let r = tick(ms(50));
let fired = r.recv().unwrap();
assert!(start < fired);
assert!(fired - start >= ms(50));
let now = Instant::now();
assert!(fired < now);
assert!(now - fired < fired - start);
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow
#[test]
fn recv_timeout() {
let start = Instant::now();
let r = tick(ms(200));
assert!(r.recv_timeout(ms(100)).is_err());
let now = Instant::now();
assert!(now - start >= ms(100));
assert!(now - start <= ms(150));
let fired = r.recv_timeout(ms(200)).unwrap();
assert!(fired - start >= ms(200));
assert!(fired - start <= ms(250));
assert!(r.recv_timeout(ms(100)).is_err());
let now = Instant::now();
assert!(now - start >= ms(300));
assert!(now - start <= ms(350));
let fired = r.recv_timeout(ms(200)).unwrap();
assert!(fired - start >= ms(400));
assert!(fired - start <= ms(450));
}
#[test]
fn recv_two() {
let r1 = tick(ms(50));
let r2 = tick(ms(50));
scope(|scope| {
scope.spawn(|_| {
for _ in 0..10 {
select! {
recv(r1) -> _ => {}
recv(r2) -> _ => {}
}
}
});
scope.spawn(|_| {
for _ in 0..10 {
select! {
recv(r1) -> _ => {}
recv(r2) -> _ => {}
}
}
});
})
.unwrap();
}
#[test]
fn recv_race() {
select! {
recv(tick(ms(50))) -> _ => {}
recv(tick(ms(100))) -> _ => panic!(),
}
select! {
recv(tick(ms(100))) -> _ => panic!(),
recv(tick(ms(50))) -> _ => {}
}
}
#[test]
fn stress_default() {
const COUNT: usize = 10;
for _ in 0..COUNT {
select! {
recv(tick(ms(0))) -> _ => {}
default => panic!(),
}
}
for _ in 0..COUNT {
select! {
recv(tick(ms(100))) -> _ => panic!(),
default => {}
}
}
}
#[test]
fn select() {
const THREADS: usize = 4;
let hits = AtomicUsize::new(0);
let r1 = tick(ms(200));
let r2 = tick(ms(300));
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
let timeout = after(ms(1100));
loop {
let mut sel = Select::new();
let oper1 = sel.recv(&r1);
let oper2 = sel.recv(&r2);
let oper3 = sel.recv(&timeout);
let oper = sel.select();
match oper.index() {
i if i == oper1 => {
oper.recv(&r1).unwrap();
hits.fetch_add(1, Ordering::SeqCst);
}
i if i == oper2 => {
oper.recv(&r2).unwrap();
hits.fetch_add(1, Ordering::SeqCst);
}
i if i == oper3 => {
oper.recv(&timeout).unwrap();
break;
}
_ => unreachable!(),
}
}
});
}
})
.unwrap();
assert_eq!(hits.load(Ordering::SeqCst), 8);
}
#[cfg(not(crossbeam_sanitize))] // TODO: assertions failed due to tsan is slow
#[test]
fn ready() {
const THREADS: usize = 4;
let hits = AtomicUsize::new(0);
let r1 = tick(ms(200));
let r2 = tick(ms(300));
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
let timeout = after(ms(1100));
'outer: loop {
let mut sel = Select::new();
sel.recv(&r1);
sel.recv(&r2);
sel.recv(&timeout);
loop {
match sel.ready() {
0 => {
if r1.try_recv().is_ok() {
hits.fetch_add(1, Ordering::SeqCst);
break;
}
}
1 => {
if r2.try_recv().is_ok() {
hits.fetch_add(1, Ordering::SeqCst);
break;
}
}
2 => {
if timeout.try_recv().is_ok() {
break 'outer;
}
}
_ => unreachable!(),
}
}
}
});
}
})
.unwrap();
assert_eq!(hits.load(Ordering::SeqCst), 8);
}
#[test]
fn fairness() {
const COUNT: usize = 30;
for &dur in &[0, 1] {
let mut hits = [0usize; 2];
for _ in 0..COUNT {
let r1 = tick(ms(dur));
let r2 = tick(ms(dur));
for _ in 0..COUNT {
select! {
recv(r1) -> _ => hits[0] += 1,
recv(r2) -> _ => hits[1] += 1,
}
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
}
#[test]
fn fairness_duplicates() {
const COUNT: usize = 30;
for &dur in &[0, 1] {
let mut hits = [0usize; 5];
for _ in 0..COUNT {
let r = tick(ms(dur));
for _ in 0..COUNT {
select! {
recv(r) -> _ => hits[0] += 1,
recv(r) -> _ => hits[1] += 1,
recv(r) -> _ => hits[2] += 1,
recv(r) -> _ => hits[3] += 1,
recv(r) -> _ => hits[4] += 1,
}
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}
}

View File

@@ -0,0 +1,587 @@
//! Tests for the zero channel flavor.
use std::any::Any;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;
use crossbeam_channel::{bounded, select, Receiver};
use crossbeam_channel::{RecvError, RecvTimeoutError, TryRecvError};
use crossbeam_channel::{SendError, SendTimeoutError, TrySendError};
use crossbeam_utils::thread::scope;
use rand::{thread_rng, Rng};
fn ms(ms: u64) -> Duration {
Duration::from_millis(ms)
}
#[test]
fn smoke() {
let (s, r) = bounded(0);
assert_eq!(s.try_send(7), Err(TrySendError::Full(7)));
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
}
#[test]
fn capacity() {
let (s, r) = bounded::<()>(0);
assert_eq!(s.capacity(), Some(0));
assert_eq!(r.capacity(), Some(0));
}
#[test]
fn len_empty_full() {
let (s, r) = bounded(0);
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert!(s.is_full());
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(r.is_full());
scope(|scope| {
scope.spawn(|_| s.send(0).unwrap());
scope.spawn(|_| r.recv().unwrap());
})
.unwrap();
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert!(s.is_full());
assert_eq!(r.len(), 0);
assert!(r.is_empty());
assert!(r.is_full());
}
#[test]
fn try_recv() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.try_recv(), Err(TryRecvError::Empty));
thread::sleep(ms(1500));
assert_eq!(r.try_recv(), Ok(7));
thread::sleep(ms(500));
assert_eq!(r.try_recv(), Err(TryRecvError::Disconnected));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn recv() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Ok(7));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(8));
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(9));
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
s.send(8).unwrap();
s.send(9).unwrap();
});
})
.unwrap();
}
#[test]
fn recv_timeout() {
let (s, r) = bounded::<i32>(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv_timeout(ms(1000)), Err(RecvTimeoutError::Timeout));
assert_eq!(r.recv_timeout(ms(1000)), Ok(7));
assert_eq!(
r.recv_timeout(ms(1000)),
Err(RecvTimeoutError::Disconnected)
);
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
s.send(7).unwrap();
});
})
.unwrap();
}
#[test]
fn try_send() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(s.try_send(7), Err(TrySendError::Full(7)));
thread::sleep(ms(1500));
assert_eq!(s.try_send(8), Ok(()));
thread::sleep(ms(500));
assert_eq!(s.try_send(9), Err(TrySendError::Disconnected(9)));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
assert_eq!(r.recv(), Ok(8));
});
})
.unwrap();
}
#[test]
fn send() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
s.send(7).unwrap();
thread::sleep(ms(1000));
s.send(8).unwrap();
thread::sleep(ms(1000));
s.send(9).unwrap();
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
assert_eq!(r.recv(), Ok(7));
assert_eq!(r.recv(), Ok(8));
assert_eq!(r.recv(), Ok(9));
});
})
.unwrap();
}
#[test]
fn send_timeout() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(
s.send_timeout(7, ms(1000)),
Err(SendTimeoutError::Timeout(7))
);
assert_eq!(s.send_timeout(8, ms(1000)), Ok(()));
assert_eq!(
s.send_timeout(9, ms(1000)),
Err(SendTimeoutError::Disconnected(9))
);
});
scope.spawn(move |_| {
thread::sleep(ms(1500));
assert_eq!(r.recv(), Ok(8));
});
})
.unwrap();
}
#[test]
fn len() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
let (s, r) = bounded(0);
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
assert_eq!(r.recv(), Ok(i));
assert_eq!(r.len(), 0);
}
});
scope.spawn(|_| {
for i in 0..COUNT {
s.send(i).unwrap();
assert_eq!(s.len(), 0);
}
});
})
.unwrap();
assert_eq!(s.len(), 0);
assert_eq!(r.len(), 0);
}
#[test]
fn disconnect_wakes_sender() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(s.send(()), Err(SendError(())));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
drop(r);
});
})
.unwrap();
}
#[test]
fn disconnect_wakes_receiver() {
let (s, r) = bounded::<()>(0);
scope(|scope| {
scope.spawn(move |_| {
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
thread::sleep(ms(1000));
drop(s);
});
})
.unwrap();
}
#[test]
fn spsc() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 100_000;
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
for i in 0..COUNT {
assert_eq!(r.recv(), Ok(i));
}
assert_eq!(r.recv(), Err(RecvError));
});
scope.spawn(move |_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
})
.unwrap();
}
#[test]
fn mpmc() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 25_000;
const THREADS: usize = 4;
let (s, r) = bounded::<usize>(0);
let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::<Vec<_>>();
scope(|scope| {
for _ in 0..THREADS {
scope.spawn(|_| {
for _ in 0..COUNT {
let n = r.recv().unwrap();
v[n].fetch_add(1, Ordering::SeqCst);
}
});
}
for _ in 0..THREADS {
scope.spawn(|_| {
for i in 0..COUNT {
s.send(i).unwrap();
}
});
}
})
.unwrap();
for c in v {
assert_eq!(c.load(Ordering::SeqCst), THREADS);
}
}
#[test]
fn stress_oneshot() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
for _ in 0..COUNT {
let (s, r) = bounded(1);
scope(|scope| {
scope.spawn(|_| r.recv().unwrap());
scope.spawn(|_| s.send(0).unwrap());
})
.unwrap();
}
}
#[test]
fn stress_iter() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
let (request_s, request_r) = bounded(0);
let (response_s, response_r) = bounded(0);
scope(|scope| {
scope.spawn(move |_| {
let mut count = 0;
loop {
for x in response_r.try_iter() {
count += x;
if count == COUNT {
return;
}
}
let _ = request_s.try_send(());
}
});
for _ in request_r.iter() {
if response_s.send(1).is_err() {
break;
}
}
})
.unwrap();
}
#[test]
fn stress_timeout_two_threads() {
const COUNT: usize = 100;
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
loop {
if let Ok(()) = s.send_timeout(i, ms(10)) {
break;
}
}
}
});
scope.spawn(|_| {
for i in 0..COUNT {
if i % 2 == 0 {
thread::sleep(ms(50));
}
loop {
if let Ok(x) = r.recv_timeout(ms(10)) {
assert_eq!(x, i);
break;
}
}
}
});
})
.unwrap();
}
#[test]
fn drops() {
#[cfg(miri)]
const RUNS: usize = 20;
#[cfg(not(miri))]
const RUNS: usize = 100;
#[cfg(miri)]
const STEPS: usize = 100;
#[cfg(not(miri))]
const STEPS: usize = 10_000;
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, PartialEq)]
struct DropCounter;
impl Drop for DropCounter {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
let mut rng = thread_rng();
for _ in 0..RUNS {
let steps = rng.gen_range(0..STEPS);
DROPS.store(0, Ordering::SeqCst);
let (s, r) = bounded::<DropCounter>(0);
scope(|scope| {
scope.spawn(|_| {
for _ in 0..steps {
r.recv().unwrap();
}
});
scope.spawn(|_| {
for _ in 0..steps {
s.send(DropCounter).unwrap();
}
});
})
.unwrap();
assert_eq!(DROPS.load(Ordering::SeqCst), steps);
drop(s);
drop(r);
assert_eq!(DROPS.load(Ordering::SeqCst), steps);
}
}
#[test]
fn fairness() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s1, r1) = bounded::<()>(0);
let (s2, r2) = bounded::<()>(0);
scope(|scope| {
scope.spawn(|_| {
let mut hits = [0usize; 2];
for _ in 0..COUNT {
select! {
recv(r1) -> _ => hits[0] += 1,
recv(r2) -> _ => hits[1] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
});
let mut hits = [0usize; 2];
for _ in 0..COUNT {
select! {
send(s1, ()) -> _ => hits[0] += 1,
send(s2, ()) -> _ => hits[1] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
})
.unwrap();
}
#[test]
fn fairness_duplicates() {
#[cfg(miri)]
const COUNT: usize = 100;
#[cfg(not(miri))]
const COUNT: usize = 10_000;
let (s, r) = bounded::<()>(0);
scope(|scope| {
scope.spawn(|_| {
let mut hits = [0usize; 5];
for _ in 0..COUNT {
select! {
recv(r) -> _ => hits[0] += 1,
recv(r) -> _ => hits[1] += 1,
recv(r) -> _ => hits[2] += 1,
recv(r) -> _ => hits[3] += 1,
recv(r) -> _ => hits[4] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
});
let mut hits = [0usize; 5];
for _ in 0..COUNT {
select! {
send(s, ()) -> _ => hits[0] += 1,
send(s, ()) -> _ => hits[1] += 1,
send(s, ()) -> _ => hits[2] += 1,
send(s, ()) -> _ => hits[3] += 1,
send(s, ()) -> _ => hits[4] += 1,
}
}
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
})
.unwrap();
}
#[test]
fn recv_in_send() {
let (s, r) = bounded(0);
scope(|scope| {
scope.spawn(|_| {
thread::sleep(ms(100));
r.recv()
});
scope.spawn(|_| {
thread::sleep(ms(500));
s.send(()).unwrap();
});
select! {
send(s, r.recv().unwrap()) -> _ => {}
}
})
.unwrap();
}
#[test]
fn channel_through_channel() {
#[cfg(miri)]
const COUNT: usize = 50;
#[cfg(not(miri))]
const COUNT: usize = 1000;
type T = Box<dyn Any + Send>;
let (s, r) = bounded::<T>(0);
scope(|scope| {
scope.spawn(move |_| {
let mut s = s;
for _ in 0..COUNT {
let (new_s, new_r) = bounded(0);
let new_r: T = Box::new(Some(new_r));
s.send(new_r).unwrap();
s = new_s;
}
});
scope.spawn(move |_| {
let mut r = r;
for _ in 0..COUNT {
r = r
.recv()
.unwrap()
.downcast_mut::<Option<Receiver<T>>>()
.unwrap()
.take()
.unwrap()
}
});
})
.unwrap();
}