Skip to content

Commit 45a1c33

Browse files
bors[bot]Aaron1011cuviper
committed
674: Make test 'iter_panic_fuse' deterministic r=cuviper a=Aaron1011 Fixes rayon-rs#667 'panic_fuse' will only stop other threads 'as soon as possible' - if the other threads are fast enough, they might end up processing the entire rest of the iterator. This commit changes the test 'iter_panic_fuse' to properly take this into account, by creating a custom threadpool with only 1 thread. This makes the test deterministic - with only one thread, the panic is guaranmteed to be observed when the next item is processed, causing the desired early exit. Co-authored-by: Aaron Hill <aa1ronham@gmail.com> Co-authored-by: Josh Stone <cuviper@gmail.com>
2 parents 2b5227c + 2a2c5a8 commit 45a1c33

File tree

2 files changed

+91
-48
lines changed

2 files changed

+91
-48
lines changed

src/iter/test.rs

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -447,53 +447,89 @@ fn check_cmp_gt_to_seq() {
447447

448448
#[test]
449449
fn check_cmp_short_circuit() {
450+
// We only use a single thread in order to make the short-circuit behavior deterministic.
451+
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
452+
450453
let a = vec![0; 1024];
451454
let mut b = a.clone();
452455
b[42] = 1;
453456

454-
let counter = AtomicUsize::new(0);
455-
let result = a
456-
.par_iter()
457-
.inspect(|_| {
458-
counter.fetch_add(1, Ordering::SeqCst);
459-
})
460-
.cmp(&b);
461-
assert!(result == ::std::cmp::Ordering::Less);
462-
assert!(counter.load(Ordering::SeqCst) < a.len()); // should not have visited every single one
457+
pool.install(|| {
458+
let expected = ::std::cmp::Ordering::Less;
459+
assert_eq!(a.par_iter().cmp(&b), expected);
460+
461+
for len in 1..10 {
462+
let counter = AtomicUsize::new(0);
463+
let result = a
464+
.par_iter()
465+
.with_max_len(len)
466+
.inspect(|_| {
467+
counter.fetch_add(1, Ordering::SeqCst);
468+
})
469+
.cmp(&b);
470+
assert_eq!(result, expected);
471+
// should not have visited every single one
472+
assert!(counter.into_inner() < a.len());
473+
}
474+
});
463475
}
464476

465477
#[test]
466478
fn check_partial_cmp_short_circuit() {
479+
// We only use a single thread to make the short-circuit behavior deterministic.
480+
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
481+
467482
let a = vec![0; 1024];
468483
let mut b = a.clone();
469484
b[42] = 1;
470485

471-
let counter = AtomicUsize::new(0);
472-
let result = a
473-
.par_iter()
474-
.inspect(|_| {
475-
counter.fetch_add(1, Ordering::SeqCst);
476-
})
477-
.partial_cmp(&b);
478-
assert!(result == Some(::std::cmp::Ordering::Less));
479-
assert!(counter.load(Ordering::SeqCst) < a.len()); // should not have visited every single one
486+
pool.install(|| {
487+
let expected = Some(::std::cmp::Ordering::Less);
488+
assert_eq!(a.par_iter().partial_cmp(&b), expected);
489+
490+
for len in 1..10 {
491+
let counter = AtomicUsize::new(0);
492+
let result = a
493+
.par_iter()
494+
.with_max_len(len)
495+
.inspect(|_| {
496+
counter.fetch_add(1, Ordering::SeqCst);
497+
})
498+
.partial_cmp(&b);
499+
assert_eq!(result, expected);
500+
// should not have visited every single one
501+
assert!(counter.into_inner() < a.len());
502+
}
503+
});
480504
}
481505

482506
#[test]
483507
fn check_partial_cmp_nan_short_circuit() {
508+
// We only use a single thread to make the short-circuit behavior deterministic.
509+
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
510+
484511
let a = vec![0.0; 1024];
485512
let mut b = a.clone();
486513
b[42] = f64::NAN;
487514

488-
let counter = AtomicUsize::new(0);
489-
let result = a
490-
.par_iter()
491-
.inspect(|_| {
492-
counter.fetch_add(1, Ordering::SeqCst);
493-
})
494-
.partial_cmp(&b);
495-
assert!(result == None);
496-
assert!(counter.load(Ordering::SeqCst) < a.len()); // should not have visited every single one
515+
pool.install(|| {
516+
let expected = None;
517+
assert_eq!(a.par_iter().partial_cmp(&b), expected);
518+
519+
for len in 1..10 {
520+
let counter = AtomicUsize::new(0);
521+
let result = a
522+
.par_iter()
523+
.with_max_len(len)
524+
.inspect(|_| {
525+
counter.fetch_add(1, Ordering::SeqCst);
526+
})
527+
.partial_cmp(&b);
528+
assert_eq!(result, expected);
529+
// should not have visited every single one
530+
assert!(counter.into_inner() < a.len());
531+
}
532+
});
497533
}
498534

499535
#[test]

tests/iter_panic.rs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
extern crate rayon;
22

33
use rayon::prelude::*;
4+
use rayon::ThreadPoolBuilder;
45
use std::ops::Range;
56
use std::panic::{self, UnwindSafe};
67
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -22,26 +23,32 @@ fn iter_panic() {
2223

2324
#[test]
2425
fn iter_panic_fuse() {
25-
fn count(iter: impl ParallelIterator + UnwindSafe) -> usize {
26-
let count = AtomicUsize::new(0);
27-
let result = panic::catch_unwind(|| {
28-
iter.for_each(|_| {
29-
count.fetch_add(1, Ordering::Relaxed);
26+
// We only use a single thread in order to make the behavior
27+
// of 'panic_fuse' deterministic
28+
let pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
29+
30+
pool.install(|| {
31+
fn count(iter: impl ParallelIterator + UnwindSafe) -> usize {
32+
let count = AtomicUsize::new(0);
33+
let result = panic::catch_unwind(|| {
34+
iter.for_each(|_| {
35+
count.fetch_add(1, Ordering::Relaxed);
36+
});
3037
});
31-
});
32-
assert!(result.is_err());
33-
count.into_inner()
34-
}
35-
36-
// Without `panic_fuse()`, we'll reach every item except the panicking one.
37-
let expected = ITER.len() - 1;
38-
let iter = ITER.into_par_iter().with_max_len(1);
39-
assert_eq!(count(iter.clone().inspect(check)), expected);
40-
41-
// With `panic_fuse()` anywhere in the chain, we'll reach fewer items.
42-
assert!(count(iter.clone().inspect(check).panic_fuse()) < expected);
43-
assert!(count(iter.clone().panic_fuse().inspect(check)) < expected);
44-
45-
// Try in reverse to be sure we hit the producer case.
46-
assert!(count(iter.clone().panic_fuse().inspect(check).rev()) < expected);
38+
assert!(result.is_err());
39+
count.into_inner()
40+
}
41+
42+
// Without `panic_fuse()`, we'll reach every item except the panicking one.
43+
let expected = ITER.len() - 1;
44+
let iter = ITER.into_par_iter().with_max_len(1);
45+
assert_eq!(count(iter.clone().inspect(check)), expected);
46+
47+
// With `panic_fuse()` anywhere in the chain, we'll reach fewer items.
48+
assert!(count(iter.clone().inspect(check).panic_fuse()) < expected);
49+
assert!(count(iter.clone().panic_fuse().inspect(check)) < expected);
50+
51+
// Try in reverse to be sure we hit the producer case.
52+
assert!(count(iter.clone().panic_fuse().inspect(check).rev()) < expected);
53+
});
4754
}

0 commit comments

Comments
 (0)