#![cfg(windows)]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::time::{Duration, Instant, SystemTime};
use windows_threadpool_sys::cleanup_group::CleanupGroup;
use windows_threadpool_sys::timer::{ThreadpoolPeriodicTimer, ThreadpoolTimer};
const ENABLE_VAR: &str = "WINDOWS_THREADPOOL_STRESS";
const SCALE_VAR: &str = "WINDOWS_THREADPOOL_STRESS_SCALE";
fn enabled() -> bool {
std::env::var_os(ENABLE_VAR).is_some_and(|raw| {
matches!(
raw.to_string_lossy().trim().to_ascii_lowercase().as_str(),
"1" | "true" | "yes" | "on"
)
})
}
fn scale() -> usize {
std::env::var(SCALE_VAR)
.ok()
.and_then(|raw| raw.trim().parse::<usize>().ok())
.filter(|&n| n > 0)
.unwrap_or(1)
}
fn load(base: usize) -> usize {
base.saturating_mul(scale())
}
static LANE: Mutex<()> = Mutex::new(());
fn enter_lane(name: &str) -> Option<MutexGuard<'static, ()>> {
if !enabled() {
eprintln!("stress: skipping {name} -- set {ENABLE_VAR}=1 to run");
return None;
}
let lane = LANE.lock().unwrap_or_else(|poison| poison.into_inner());
eprintln!("stress: running {name} at scale {}", scale());
Some(lane)
}
macro_rules! stress {
($(#[$meta:meta])* $name:ident $body:block) => {
$(#[$meta])*
#[test]
fn $name() {
let Some(_lane) = enter_lane(stringify!($name)) else {
return;
};
let started = Instant::now();
$body
eprintln!("stress: {} finished in {:?}", stringify!($name), started.elapsed());
}
};
}
struct Tally {
count: Mutex<usize>,
changed: Condvar,
}
impl Tally {
fn new() -> Arc<Self> {
Arc::new(Self {
count: Mutex::new(0),
changed: Condvar::new(),
})
}
fn record(&self) -> usize {
let mut count = self.count.lock().unwrap_or_else(|p| p.into_inner());
*count += 1;
let now = *count;
self.changed.notify_all();
now
}
fn count(&self) -> usize {
*self.count.lock().unwrap_or_else(|p| p.into_inner())
}
fn wait_for(&self, target: usize, timeout: Duration) -> usize {
let deadline = Instant::now() + timeout;
let mut count = self.count.lock().unwrap_or_else(|p| p.into_inner());
while *count < target {
let Some(remaining) = deadline.checked_duration_since(Instant::now()) else {
break;
};
let (next, _) = self
.changed
.wait_timeout(count, remaining)
.unwrap_or_else(|p| p.into_inner());
count = next;
}
*count
}
}
struct Overlap {
inside: AtomicBool,
violations: AtomicUsize,
peak_concurrent: AtomicUsize,
concurrent: AtomicUsize,
}
impl Overlap {
fn new() -> Arc<Self> {
Arc::new(Self {
inside: AtomicBool::new(false),
violations: AtomicUsize::new(0),
peak_concurrent: AtomicUsize::new(0),
concurrent: AtomicUsize::new(0),
})
}
fn enter(self: &Arc<Self>) -> OverlapGuard<'_> {
if self.inside.swap(true, Ordering::SeqCst) {
self.violations.fetch_add(1, Ordering::SeqCst);
}
let now = self.concurrent.fetch_add(1, Ordering::SeqCst) + 1;
self.peak_concurrent.fetch_max(now, Ordering::SeqCst);
OverlapGuard(self)
}
fn violations(&self) -> usize {
self.violations.load(Ordering::SeqCst)
}
fn peak_concurrent(&self) -> usize {
self.peak_concurrent.load(Ordering::SeqCst)
}
}
struct OverlapGuard<'a>(&'a Arc<Overlap>);
impl Drop for OverlapGuard<'_> {
fn drop(&mut self) {
self.0.concurrent.fetch_sub(1, Ordering::SeqCst);
self.0.inside.store(false, Ordering::SeqCst);
}
}
fn work_for(step: usize) {
let micros = u64::try_from(step % 5).unwrap_or(0) * 200;
if micros > 0 {
std::thread::sleep(Duration::from_micros(micros));
}
}
fn assert_quiescent(tally: &Tally, label: &str) {
let settled = tally.count();
std::thread::sleep(Duration::from_millis(50));
assert_eq!(
tally.count(),
settled,
"{label}: a callback ran after the timer was drained"
);
}
stress! {
stress_one_shot_self_rearm_never_overlaps {
let target = load(300);
let overlap = Overlap::new();
let tally = Tally::new();
let seen = Arc::clone(&overlap);
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |firing| {
let _inside = seen.enter();
let n = counter.record();
work_for(n);
if n < target {
firing.rearm_after(Duration::ZERO);
}
},
None,
)
.expect("create timer");
let chain_started = Instant::now();
timer.set_after(Duration::ZERO);
let reached = tally.wait_for(target, Duration::from_secs(300));
let chain_elapsed = chain_started.elapsed();
timer.disarm();
timer.cancel_pending();
assert_eq!(
overlap.violations(),
0,
"a self-re-arming one-shot overlapped itself"
);
assert_eq!(reached, target, "the re-arm chain stalled");
assert_quiescent(&tally, "self-re-arm chain");
eprintln!(
"stress: {reached} links in {chain_elapsed:?} ({:?} mean per link)",
chain_elapsed / u32::try_from(reached.max(1)).unwrap_or(1)
);
}
}
stress! {
stress_one_shot_rearm_at_past_instants {
let target = load(200);
let overlap = Overlap::new();
let tally = Tally::new();
let seen = Arc::clone(&overlap);
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |firing| {
let _inside = seen.enter();
let n = counter.record();
if n < target {
let past = SystemTime::now() - Duration::from_secs(1);
firing.rearm_at(past);
}
},
None,
)
.expect("create timer");
timer.set_at(SystemTime::now() - Duration::from_secs(1));
let reached = tally.wait_for(target, Duration::from_secs(300));
timer.disarm();
timer.cancel_pending();
assert_eq!(overlap.violations(), 0, "a past-instant chain overlapped");
assert_eq!(reached, target, "the past-instant chain stalled");
assert_quiescent(&tally, "past-instant chain");
}
}
stress! {
stress_one_shot_external_arming_churn {
let threads = 8;
let per_thread = load(400);
let tally = Tally::new();
let overlap = Overlap::new();
let counter = Arc::clone(&tally);
let seen = Arc::clone(&overlap);
let timer = Arc::new(
ThreadpoolTimer::new(
move |_firing| {
let _inside = seen.enter();
let n = counter.record();
work_for(n);
},
None,
)
.expect("create timer"),
);
let workers: Vec<_> = (0..threads)
.map(|t| {
let timer = Arc::clone(&timer);
std::thread::spawn(move || {
for i in 0..per_thread {
match (t + i) % 8 {
0..=2 => timer.set_after(Duration::ZERO),
3 => timer.set_after(Duration::from_micros(50)),
4 => timer.disarm(),
_ => {
let _ = timer.is_set();
}
}
if i % 4 == 0 {
std::thread::sleep(Duration::from_millis(5));
}
}
})
})
.collect();
for worker in workers {
worker.join().expect("arming thread");
}
timer.disarm();
timer.cancel_pending();
let fired = tally.count();
eprintln!(
"stress: fired {fired} times under arming churn; {} overlapping entries, peak {} concurrent",
overlap.violations(),
overlap.peak_concurrent()
);
assert!(
fired > 0,
"the timer never fired under arming churn -- the loop is outrunning the pool"
);
assert!(!timer.is_set(), "the timer is still armed after disarm");
assert_quiescent(&tally, "arming churn");
}
}
stress! {
stress_one_shot_arm_disarm_race {
let rounds = load(20_000);
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = Arc::new(
ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer"),
);
let armer = {
let timer = Arc::clone(&timer);
std::thread::spawn(move || {
for _ in 0..rounds {
timer.set_after(Duration::ZERO);
}
})
};
let disarmer = {
let timer = Arc::clone(&timer);
std::thread::spawn(move || {
for _ in 0..rounds {
timer.disarm();
}
})
};
armer.join().expect("arming thread");
disarmer.join().expect("disarming thread");
timer.disarm();
timer.cancel_pending();
eprintln!("stress: fired {} times across {rounds} races", tally.count());
assert!(!timer.is_set(), "the timer is still armed after disarm");
assert_quiescent(&tally, "arm/disarm race");
}
}
stress! {
stress_one_shot_arm_and_await_fire {
let threads = 8;
let rounds = load(40);
let workers: Vec<_> = (0..threads)
.map(|_| {
std::thread::spawn(move || {
let tally = Tally::new();
let overlap = Overlap::new();
let counter = Arc::clone(&tally);
let seen = Arc::clone(&overlap);
let timer = ThreadpoolTimer::new(
move |_firing| {
{
let _inside = seen.enter();
work_for(counter.count());
}
counter.record();
},
None,
)
.expect("create timer");
for round in 1..=rounds {
timer.set_after(Duration::ZERO);
let reached = tally.wait_for(round, Duration::from_secs(30));
assert_eq!(reached, round, "a timer stopped firing mid-cycle");
}
timer.disarm();
timer.cancel_pending();
assert_eq!(
overlap.violations(),
0,
"a timer overlapped itself across arm-fire cycles"
);
tally.count()
})
})
.collect();
let total: usize = workers
.into_iter()
.map(|worker| worker.join().expect("cycle thread"))
.sum();
assert_eq!(total, threads * rounds, "not every arm-fire cycle completed");
eprintln!("stress: completed {total} arm-fire cycles across {threads} threads");
}
}
stress! {
stress_one_shot_many_timers_fire {
let count = load(2_000);
let tally = Tally::new();
let timers: Vec<_> = (0..count)
.map(|i| {
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer");
timer.set_after(Duration::from_micros(u64::try_from(i % 500).unwrap_or(0) * 20));
timer
})
.collect();
let fired = tally.wait_for(count, Duration::from_secs(120));
for timer in &timers {
timer.disarm();
timer.cancel_pending();
}
assert_eq!(fired, count, "not every timer fired");
assert_quiescent(&tally, "many timers");
}
}
stress! {
stress_one_shot_coalescing_windows {
let count = load(1_000);
let tally = Tally::new();
let timers: Vec<_> = (0..count)
.map(|i| {
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer");
timer.set_after_with_window(
Duration::from_millis(u64::try_from(i % 10).unwrap_or(0)),
Duration::from_millis(50),
);
timer
})
.collect();
let fired = tally.wait_for(count, Duration::from_secs(120));
for timer in &timers {
timer.disarm();
timer.cancel_pending();
}
assert_eq!(fired, count, "a coalesced timer never fired");
assert_quiescent(&tally, "coalescing windows");
}
}
stress! {
stress_one_shot_rapid_create_arm_drop {
let cycles = load(5_000);
let tally = Tally::new();
for i in 0..cycles {
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer");
match i % 3 {
0 => timer.set_after(Duration::ZERO),
1 => timer.set_after(Duration::from_millis(16)),
_ => timer.set_after(Duration::from_millis(50)),
}
drop(timer);
}
eprintln!(
"stress: {cycles} create/arm/drop cycles, {} callbacks ran",
tally.count()
);
}
}
stress! {
stress_one_shot_drop_racing_the_due_time {
let cycles = load(200);
let tally = Tally::new();
for i in 0..cycles {
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer");
timer.set_after(Duration::from_millis(16));
let offset = u64::try_from(i % 8).unwrap_or(0) * 4;
std::thread::sleep(Duration::from_millis(offset));
drop(timer);
}
let fired = tally.count();
eprintln!("stress: {cycles} drops straddling the due time, {fired} callbacks ran");
assert!(
fired > 0,
"no drop landed after a firing -- the offsets no longer span the tick"
);
}
}
stress! {
stress_one_shot_drop_during_rearming_callback {
let cycles = load(300);
let mut entered_total = 0usize;
for _ in 0..cycles {
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |firing| {
counter.record();
firing.rearm_after(Duration::ZERO);
std::thread::sleep(Duration::from_millis(5));
},
None,
)
.expect("create timer");
timer.set_after(Duration::ZERO);
let entered = tally.wait_for(1, Duration::from_secs(30));
assert_eq!(entered, 1, "the callback never ran");
entered_total += entered;
drop(timer);
let after = tally.count();
std::thread::sleep(Duration::from_millis(25));
assert_eq!(
tally.count(),
after,
"a callback ran after the timer was dropped"
);
}
eprintln!("stress: {cycles} drops landed mid-callback ({entered_total} callbacks entered)");
}
}
stress! {
stress_one_shot_concurrent_teardown {
let threads = 8;
let per_thread = load(300);
let tally = Tally::new();
let workers: Vec<_> = (0..threads)
.map(|t| {
let tally = Arc::clone(&tally);
std::thread::spawn(move || {
for i in 0..per_thread {
let counter = Arc::clone(&tally);
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create timer");
timer.set_after(Duration::ZERO);
match (t + i) % 3 {
0 => timer.cancel_pending(),
1 => {
std::thread::sleep(Duration::from_millis(20));
timer.disarm();
timer.wait();
}
_ => {}
}
drop(timer);
}
})
})
.collect();
for worker in workers {
worker.join().expect("teardown thread");
}
let fired = tally.count();
eprintln!(
"stress: {} concurrent teardowns, {fired} callbacks ran",
threads * per_thread
);
assert!(
fired > 0,
"no teardown raced a firing -- every cycle outran the pool"
);
}
}
stress! {
stress_periodic_sustained_ticking {
let target = load(500);
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer");
let started = Instant::now();
timer.start_after(Duration::ZERO);
let reached = tally.wait_for(target, Duration::from_secs(300));
let elapsed = started.elapsed();
timer.stop_and_drain();
assert_eq!(reached, target, "the timer stopped ticking");
assert!(!timer.is_running(), "the timer still reports running");
assert_quiescent(&tally, "high-frequency ticks");
eprintln!(
"stress: {reached} ticks in {elapsed:?} ({:?} mean per tick)",
elapsed / u32::try_from(reached.max(1)).unwrap_or(1)
);
}
}
stress! {
stress_periodic_overlapping_ticks_are_tolerated {
let target = load(200);
let tally = Tally::new();
let overlap = Overlap::new();
let counter = Arc::clone(&tally);
let seen = Arc::clone(&overlap);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |_tick| {
let _inside = seen.enter();
counter.record();
std::thread::sleep(Duration::from_millis(20));
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::ZERO);
let reached = tally.wait_for(target, Duration::from_secs(300));
timer.stop_and_drain();
assert_eq!(reached, target, "the timer stopped ticking");
assert_quiescent(&tally, "overlapping ticks");
eprintln!(
"stress: {reached} ticks, {} overlapping entries, peak {} concurrent",
overlap.violations(),
overlap.peak_concurrent()
);
}
}
stress! {
stress_periodic_self_stop {
let rounds = load(200);
let mut overshoot_total = 0usize;
for _ in 0..rounds {
let threshold = 5;
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |tick| {
if counter.record() >= threshold {
tick.stop();
}
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::ZERO);
let reached = tally.wait_for(threshold, Duration::from_secs(30));
assert!(
reached >= threshold,
"the timer stopped before reaching its threshold"
);
timer.stop_and_drain();
let settled = tally.count();
overshoot_total += settled.saturating_sub(threshold);
std::thread::sleep(Duration::from_millis(5));
assert_eq!(
tally.count(),
settled,
"the timer kept ticking after stopping itself and draining"
);
}
eprintln!(
"stress: {rounds} self-stopping timers, {overshoot_total} ticks past the threshold"
);
}
}
stress! {
stress_periodic_start_stop_churn {
let threads = 8;
let per_thread = load(200);
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = Arc::new(
ThreadpoolPeriodicTimer::new(
Duration::from_millis(2),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer"),
);
let workers: Vec<_> = (0..threads)
.map(|t| {
let timer = Arc::clone(&timer);
std::thread::spawn(move || {
for i in 0..per_thread {
match (t + i) % 8 {
0..=2 => timer.start_after(Duration::ZERO),
3 => timer.start(),
4 => timer.stop(),
_ => {
let _ = timer.is_running();
}
}
if i % 4 == 0 {
std::thread::sleep(Duration::from_millis(20));
}
}
})
})
.collect();
for worker in workers {
worker.join().expect("churn thread");
}
timer.stop_and_drain();
let fired = tally.count();
eprintln!("stress: {fired} ticks under start/stop churn");
assert!(
fired > 0,
"the timer never ticked under churn -- the loop is outrunning the pool"
);
assert!(!timer.is_running(), "the timer still reports running");
assert_quiescent(&tally, "start/stop churn");
}
}
stress! {
stress_periodic_drop_while_ticking {
let cycles = load(300);
let tally = Tally::new();
for i in 0..cycles {
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::ZERO);
let offset = u64::try_from(i % 8).unwrap_or(0) * 2;
std::thread::sleep(Duration::from_millis(offset));
drop(timer);
}
let fired = tally.count();
eprintln!("stress: {cycles} periodic timers dropped while ticking, {fired} ticks ran");
assert!(
fired > 0,
"no drop raced a tick -- the offsets no longer span the tick"
);
}
}
stress! {
stress_periodic_drop_during_tick {
let cycles = load(200);
for _ in 0..cycles {
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |_tick| {
counter.record();
std::thread::sleep(Duration::from_millis(5));
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::ZERO);
let entered = tally.wait_for(1, Duration::from_secs(30));
assert!(entered >= 1, "the tick never ran");
drop(timer);
let after = tally.count();
std::thread::sleep(Duration::from_millis(15));
assert_eq!(
tally.count(),
after,
"a tick ran after the timer was dropped"
);
}
eprintln!("stress: {cycles} drops landed mid-tick");
}
}
stress! {
stress_periodic_many_timers_tick {
let count = load(300);
let per_timer = 3;
let tally = Tally::new();
let timers: Vec<_> = (0..count)
.map(|i| {
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(2),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::from_millis(u64::try_from(i % 10).unwrap_or(0)));
timer
})
.collect();
let reached = tally.wait_for(count * per_timer, Duration::from_secs(300));
for timer in &timers {
timer.stop_and_drain();
}
assert_eq!(
reached,
count * per_timer,
"the timer population stopped ticking"
);
for timer in &timers {
assert!(!timer.is_running(), "a timer still reports running");
}
assert_quiescent(&tally, "many periodic timers");
eprintln!("stress: {reached} ticks across {count} periodic timers");
}
}
stress! {
stress_periodic_short_period_is_rejected {
let attempts = load(2_000);
let too_short = [
Duration::ZERO,
Duration::from_micros(1),
Duration::from_micros(500),
Duration::from_micros(999),
];
for i in 0..attempts {
let period = too_short[i % too_short.len()];
let rejected = ThreadpoolPeriodicTimer::new(period, |_tick| {}, None);
assert!(rejected.is_err(), "a period of {period:?} was accepted");
}
let tally = Tally::new();
let counter = Arc::clone(&tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer after rejections");
timer.start_after(Duration::ZERO);
assert!(
tally.wait_for(1, Duration::from_secs(30)) >= 1,
"a valid timer would not tick after repeated rejections"
);
timer.stop_and_drain();
eprintln!("stress: {attempts} short-period rejections");
}
}
stress! {
stress_cleanup_group_timer_members {
let rounds = load(60);
let per_round = 40;
let tally = Tally::new();
let mut released = 0usize;
for round in 0..rounds {
let mut group = CleanupGroup::new().expect("create cleanup group");
{
let one_shots: Vec<_> = (0..per_round)
.map(|_| {
let counter = Arc::clone(&tally);
group
.create_timer(move |_firing| {
counter.record();
}, None)
.expect("create timer member")
})
.collect();
let periodics: Vec<_> = (0..per_round)
.map(|_| {
let counter = Arc::clone(&tally);
group
.create_periodic_timer(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic member")
})
.collect();
for member in &one_shots {
member.set_after(Duration::ZERO);
}
for member in &periodics {
member.start_after(Duration::ZERO);
}
assert_eq!(
group.owned_resources(),
per_round * 2,
"the group is not holding every member's context"
);
if round % 2 == 0 {
std::thread::sleep(Duration::from_millis(20));
}
}
group.close_members(round % 2 == 0);
assert_eq!(
group.owned_resources(),
0,
"the group still holds member resources after release"
);
released += per_round * 2;
let settled = tally.count();
std::thread::sleep(Duration::from_millis(10));
assert_eq!(
tally.count(),
settled,
"a member callback ran after the group released it"
);
}
eprintln!(
"stress: {released} timer members across {rounds} groups, {} callbacks ran",
tally.count()
);
}
}
stress! {
stress_cleanup_group_concurrent_release {
let threads = 8;
let per_thread = load(40);
let tally = Tally::new();
let workers: Vec<_> = (0..threads)
.map(|t| {
let tally = Arc::clone(&tally);
std::thread::spawn(move || {
for i in 0..per_thread {
let mut group = CleanupGroup::new().expect("create cleanup group");
{
let counter = Arc::clone(&tally);
let one_shot = group
.create_timer(move |_firing| {
counter.record();
}, None)
.expect("create timer member");
let counter = Arc::clone(&tally);
let periodic = group
.create_periodic_timer(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic member");
one_shot.set_after(Duration::ZERO);
periodic.start_after(Duration::ZERO);
if (t + i) % 3 == 0 {
std::thread::sleep(Duration::from_millis(20));
}
}
group.close_members((t + i) % 2 == 0);
assert_eq!(
group.owned_resources(),
0,
"a concurrently released group still holds resources"
);
}
})
})
.collect();
for worker in workers {
worker.join().expect("group thread");
}
let fired = tally.count();
eprintln!(
"stress: {} groups released concurrently, {fired} callbacks ran",
threads * per_thread
);
assert!(
fired > 0,
"no group release raced a callback -- every round outran the pool"
);
}
}
stress! {
stress_cleanup_group_drop_without_close {
let rounds = load(200);
let tally = Tally::new();
for round in 0..rounds {
let group = CleanupGroup::new().expect("create cleanup group");
{
let counter = Arc::clone(&tally);
let one_shot = group
.create_timer(move |_firing| {
counter.record();
}, None)
.expect("create timer member");
let counter = Arc::clone(&tally);
let periodic = group
.create_periodic_timer(
Duration::from_millis(1),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic member");
one_shot.set_after(Duration::ZERO);
periodic.start_after(Duration::ZERO);
if round % 3 == 0 {
std::thread::sleep(Duration::from_millis(20));
}
}
drop(group);
}
let fired = tally.count();
eprintln!("stress: {rounds} groups dropped without closing, {fired} callbacks ran");
assert!(
fired > 0,
"no group drop raced a callback -- every round outran the pool"
);
}
}
stress! {
stress_mixed_timer_load {
let duration = Duration::from_secs(u64::try_from(load(3)).unwrap_or(3).min(60));
let deadline = Instant::now() + duration;
let chain_tally = Tally::new();
let chain_overlap = Overlap::new();
let tick_tally = Tally::new();
let churn_tally = Tally::new();
let counter = Arc::clone(&chain_tally);
let seen = Arc::clone(&chain_overlap);
let chain = ThreadpoolTimer::new(
move |firing| {
let _inside = seen.enter();
let n = counter.record();
work_for(n);
firing.rearm_after(Duration::ZERO);
},
None,
)
.expect("create chain timer");
chain.set_after(Duration::ZERO);
let periodics: Vec<_> = (0..16)
.map(|i| {
let counter = Arc::clone(&tick_tally);
let timer = ThreadpoolPeriodicTimer::new(
Duration::from_millis(2),
move |_tick| {
counter.record();
},
None,
)
.expect("create periodic timer");
timer.start_after(Duration::from_millis(i));
timer
})
.collect();
let workers: Vec<_> = (0..4)
.map(|t| {
let churn_tally = Arc::clone(&churn_tally);
std::thread::spawn(move || {
let mut cycles = 0usize;
while Instant::now() < deadline {
let counter = Arc::clone(&churn_tally);
if t % 2 == 0 {
let timer = ThreadpoolTimer::new(
move |_firing| {
counter.record();
},
None,
)
.expect("create churn timer");
timer.set_after(Duration::ZERO);
std::thread::sleep(Duration::from_millis(20));
drop(timer);
} else {
let mut group = CleanupGroup::new().expect("create churn group");
{
let member = group
.create_timer(move |_firing| {
counter.record();
}, None)
.expect("create churn member");
member.set_after(Duration::ZERO);
std::thread::sleep(Duration::from_millis(20));
}
group.close_members(false);
}
cycles += 1;
}
cycles
})
})
.collect();
let churn_cycles: usize = workers
.into_iter()
.map(|worker| worker.join().expect("churn thread"))
.sum();
chain.disarm();
chain.cancel_pending();
for timer in &periodics {
timer.stop_and_drain();
}
assert_eq!(
chain_overlap.violations(),
0,
"the self-re-arming one-shot overlapped itself under mixed load"
);
assert!(
chain_tally.count() > 0,
"the self-re-arming chain never advanced under mixed load"
);
assert!(
tick_tally.count() > 0,
"the periodic population never ticked under mixed load"
);
assert!(
churn_tally.count() > 0,
"no churned timer ever fired under mixed load"
);
assert_quiescent(&chain_tally, "mixed load chain");
assert_quiescent(&tick_tally, "mixed load periodics");
eprintln!(
"stress: {duration:?} of mixed load: {} chain links, {} ticks, {churn_cycles} churn cycles, {} churn firings",
chain_tally.count(),
tick_tally.count(),
churn_tally.count()
);
}
}