Skip to content

Commit a71355d

Browse files
committed
test: report a failed fair pool stress invariant instead of hanging
A worker that failed an assertion panicked before the barrier, so the other workers waited forever and the test hung. Each worker now runs its loop under catch_unwind and returns its reservation with the outcome. The coordinator joins every thread while holding all the reservations, so the consumer count and the fair limit stay fixed, then re-raises the first failure.
1 parent 21373fb commit a71355d

1 file changed

Lines changed: 33 additions & 25 deletions

File tree

‎native/core/src/execution/memory_pools/fair_pool.rs‎

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ mod tests {
341341
use std::panic::{catch_unwind, AssertUnwindSafe};
342342
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicUsize, Ordering::SeqCst};
343343
use std::sync::mpsc::{channel, Receiver, Sender};
344-
use std::sync::{Arc, Barrier};
344+
use std::sync::Arc;
345345
use std::thread;
346346
use std::time::Duration;
347347

@@ -978,42 +978,50 @@ mod tests {
978978
let reservations: Vec<_> = (0..THREADS)
979979
.map(|t| spillable(&format!("consumer-{t}"), &pool))
980980
.collect();
981-
let barrier = Arc::new(Barrier::new(THREADS));
982981

983982
let handles: Vec<_> = reservations
984983
.into_iter()
985984
.enumerate()
986985
.map(|(t, res)| {
987986
let pool = Arc::clone(&pool);
988-
let barrier = Arc::clone(&barrier);
989987
thread::spawn(move || {
990-
for i in 0..ITERS {
991-
let size = 1 + (i * 37 + t * 101) % 509;
992-
// Refusals are expected; only the invariants below must hold.
993-
let _ = res.try_grow(size);
994-
if i % 3 == 0 && res.size() > 0 {
995-
res.shrink(res.size() / 2 + 1);
988+
// Catch a failed invariant so this consumer stays registered, and the fair
989+
// limit of the others fixed, until every thread has been joined.
990+
let outcome = catch_unwind(AssertUnwindSafe(|| {
991+
for i in 0..ITERS {
992+
let size = 1 + (i * 37 + t * 101) % 509;
993+
// Refusals are expected; only the invariants below must hold.
994+
let _ = res.try_grow(size);
995+
if i % 3 == 0 && res.size() > 0 {
996+
res.shrink(res.size() / 2 + 1);
997+
}
998+
// Drains the task's balance to zero under acquires in flight.
999+
if (i + t) % 41 == 0 {
1000+
res.free();
1001+
}
1002+
// One reservation per consumer, so its size is the consumer's share.
1003+
assert!(
1004+
res.size() <= POOL_SIZE / THREADS,
1005+
"consumer exceeded its fair limit"
1006+
);
1007+
assert!(pool.reserved() <= POOL_SIZE, "pool exceeded its size");
9961008
}
997-
// Drains the task's balance to zero under acquires in flight.
998-
if (i + t) % 41 == 0 {
999-
res.free();
1000-
}
1001-
// One reservation per consumer, so its size is the consumer's share.
1002-
assert!(
1003-
res.size() <= POOL_SIZE / THREADS,
1004-
"consumer exceeded its fair limit"
1005-
);
1006-
assert!(pool.reserved() <= POOL_SIZE, "pool exceeded its size");
1007-
}
1008-
// Stay registered until every thread stops so the fair limit is fixed.
1009-
barrier.wait();
1010-
res.free();
1009+
}));
1010+
(res, outcome)
10111011
})
10121012
})
10131013
.collect();
10141014

1015-
for handle in handles {
1016-
handle.join().unwrap();
1015+
// Join every thread before dropping any reservation, then report the first failure.
1016+
let (reservations, outcomes): (Vec<_>, Vec<_>) = handles
1017+
.into_iter()
1018+
.map(|handle| handle.join().expect("worker panicked outside its loop"))
1019+
.unzip();
1020+
if let Some(panic) = outcomes.into_iter().find_map(Result::err) {
1021+
std::panic::resume_unwind(panic);
1022+
}
1023+
for res in reservations {
1024+
res.free();
10171025
}
10181026

10191027
assert_eq!(pool.reserved(), 0, "pool still tracks bytes after quiesce");

0 commit comments

Comments
 (0)