use super::*;
#[test]
fn exit_runs_when_poll_panics() {
use std::{panic, pin::pin, task::Waker};
thread_local! {
static DEPTH: Cell<isize> = const { Cell::new(0) };
static AFTER: Cell<usize> = const { Cell::new(0) };
}
set_callbacks(Some(TaskCallbacks {
before: || Some(std::ptr::null()),
enter: |p| {
DEPTH.set(DEPTH.get() + 1);
p
},
exit: |_| DEPTH.set(DEPTH.get() - 1),
after: |_| AFTER.set(AFTER.get() + 1),
}));
let mut fut = Box::pin(wrap(async { panic!("poll panic") }));
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = fut.as_mut().poll(&mut Context::from_waker(Waker::noop()));
}));
assert!(res.is_err());
assert_eq!(DEPTH.get(), 0);
drop(fut);
assert_eq!(AFTER.get(), 1);
let mut fut = pin!(wrap(async { 1 }));
let res = fut.as_mut().poll(&mut Context::from_waker(Waker::noop()));
assert_eq!(res, Poll::Ready(1));
assert_eq!(DEPTH.get(), 0);
set_callbacks(None);
}
#[test]
fn system_callbacks_wrap_spawned_tasks() {
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::{Arbiter, System, testing::TestRunner};
static DATA: u8 = 0;
static CTX: u8 = 0;
static BEFORE: AtomicUsize = AtomicUsize::new(0);
static AFTER: AtomicUsize = AtomicUsize::new(0);
static FACTORY: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static CREATED: Cell<bool> = const { Cell::new(false) };
static TRACK: Cell<bool> = const { Cell::new(false) };
static INSIDE: Cell<bool> = const { Cell::new(false) };
}
fn data() -> *const () {
(&raw const DATA).cast()
}
fn ctx() -> *const () {
(&raw const CTX).cast()
}
fn tracked<F: Future>(f: impl FnOnce() -> F) -> F {
TRACK.set(true);
let fut = f();
TRACK.set(false);
fut
}
fn factory() -> TaskCallbacks {
FACTORY.fetch_add(1, Ordering::Relaxed);
CREATED.set(true);
TaskCallbacks {
before: || {
TRACK.get().then(|| {
BEFORE.fetch_add(1, Ordering::Relaxed);
data()
})
},
enter: |p| {
assert_eq!(p, data());
INSIDE.set(true);
ctx()
},
exit: |p| {
assert_eq!(p, ctx());
INSIDE.set(false);
},
after: |p| {
assert_eq!(p, data());
AFTER.fetch_add(1, Ordering::Relaxed);
},
}
}
System::build()
.name("callbacks")
.task_callbacks(factory)
.build(TestRunner)
.block_on(async {
assert!(CREATED.get());
assert_eq!(FACTORY.load(Ordering::Relaxed), 1);
let hnd = tracked(|| crate::spawn(async { INSIDE.get() }));
assert_eq!(BEFORE.load(Ordering::Relaxed), 1);
assert!(hnd.await.unwrap());
assert!(!INSIDE.get());
assert_eq!(AFTER.load(Ordering::Relaxed), 1);
assert!(!crate::spawn(async { INSIDE.get() }).await.unwrap());
assert_eq!(BEFORE.load(Ordering::Relaxed), 1);
assert_eq!(AFTER.load(Ordering::Relaxed), 1);
let result = tracked(|| crate::spawn_sendable(async { INSIDE.get() }));
assert_eq!(BEFORE.load(Ordering::Relaxed), 2);
assert!(result.await.unwrap());
assert!(!INSIDE.get());
assert_eq!(AFTER.load(Ordering::Relaxed), 2);
let mut arb = Arbiter::new();
let inside = arb
.handle()
.spawn(async {
assert!(CREATED.get());
tracked(|| crate::spawn_sendable(async { INSIDE.get() }))
.await
.unwrap()
})
.await
.unwrap();
assert!(inside);
assert_eq!(BEFORE.load(Ordering::Relaxed), 3);
assert_eq!(FACTORY.load(Ordering::Relaxed), 2);
arb.stop();
arb.join().unwrap();
});
System::new("plain", TestRunner).block_on(async {
assert!(
!tracked(|| crate::spawn(async { INSIDE.get() }))
.await
.unwrap()
);
assert_eq!(BEFORE.load(Ordering::Relaxed), 3);
});
}