use std::cell::Cell;
use std::rc::Rc;
use nexus_async_rt::{CancellationToken, Runtime, spawn_boxed};
use nexus_rt::WorldBuilder;
struct Rng(u64);
impl Rng {
fn new(seed: u64) -> Self {
Self(seed.max(1)) }
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn shuffle<T>(&mut self, slice: &mut [T]) {
for i in (1..slice.len()).rev() {
let j = (self.next() as usize) % (i + 1);
slice.swap(i, j);
}
}
}
#[derive(Clone)]
struct DropTracker(Rc<Cell<u32>>);
impl DropTracker {
fn new(counter: &Rc<Cell<u32>>) -> Self {
Self(counter.clone())
}
}
impl Drop for DropTracker {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
struct YieldOnce {
yielded: bool,
_tracker: DropTracker,
}
impl std::future::Future for YieldOnce {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<()> {
if self.yielded {
std::task::Poll::Ready(())
} else {
self.yielded = true;
cx.waker().wake_by_ref();
std::task::Poll::Pending
}
}
}
#[test]
fn stress_fuzz_all_subsystems() {
let wb = WorldBuilder::new();
let mut world = wb.build();
let mut rt = Runtime::new(&mut world);
let drop_count = Rc::new(Cell::new(0u32));
for cycle in 0..10u32 {
let mut rng = Rng::new((cycle as u64 + 1) * 42);
let dc = drop_count.clone();
rt.block_on(async move {
let dc = dc;
for _ in 0..3 {
let t = DropTracker::new(&dc);
drop(spawn_boxed(async move {
let _keep = t;
}));
}
for _ in 0..2 {
let t = DropTracker::new(&dc);
drop(spawn_boxed(YieldOnce {
yielded: false,
_tracker: t,
}));
}
let (tx1, rx1) = nexus_async_rt::channel::local::channel::<DropTracker>(4);
let (tx2, rx2) = nexus_async_rt::channel::local::channel::<DropTracker>(4);
let dc_ch1 = dc.clone();
drop(spawn_boxed(async move {
let val = rx1.recv().await.unwrap();
let _keep = val;
let _ = dc_ch1;
}));
let dc_ch2 = dc.clone();
drop(spawn_boxed(async move {
let val = rx2.recv().await.unwrap();
let _keep = val;
let _ = dc_ch2;
}));
let token = CancellationToken::new();
let token_clone = token.clone();
let dc_cancel = dc.clone();
drop(spawn_boxed(async move {
let t = DropTracker::new(&dc_cancel);
token_clone.cancelled().await;
let _keep = t;
}));
let dc_join = dc.clone();
let mut join_handle = Some(spawn_boxed(async move {
let t = DropTracker::new(&dc_join);
nexus_async_rt::yield_now().await;
t
}));
let mut steps = [0u8, 1, 2, 3, 4, 5];
rng.shuffle(&mut steps);
for &step in &steps {
match step {
0 | 4 => {
nexus_async_rt::yield_now().await;
}
1 => {
let _ = tx1.send(DropTracker::new(&dc)).await;
}
2 => {
let _ = tx2.send(DropTracker::new(&dc)).await;
}
3 => {
token.cancel();
}
5 => {
drop(join_handle.take());
nexus_async_rt::yield_now().await;
}
_ => unreachable!(),
}
}
drop(tx1);
drop(tx2);
for _ in 0..5 {
nexus_async_rt::yield_now().await;
}
});
}
let total = drop_count.get();
assert!(
total >= 10,
"too few drops: {total} — expected at least 10 (1 per cycle)"
);
}