use super::*;
#[test]
fn load_callbacks_set_by_other_thread() {
let hnd = std::thread::spawn(|| unsafe {
task_opt_callbacks(|| Some(std::ptr::null()), |p| p, |_| {}, |_| {})
});
while Data::load().is_none() {
std::hint::spin_loop();
}
assert!(hnd.join().unwrap());
}
#[test]
fn exit_runs_when_poll_panics() {
use std::{cell::Cell, panic, pin::pin, task::Context, task::Waker};
const CHILD: &str = "NTEX_RT_TASK_EXIT_CHILD";
if std::env::var_os(CHILD).is_none() {
let out = std::process::Command::new(std::env::current_exe().unwrap())
.args(["--exact", "task::tests::exit_runs_when_poll_panics"])
.env(CHILD, "1")
.output()
.unwrap();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stdout)
);
return;
}
thread_local! {
static DEPTH: Cell<isize> = const { Cell::new(0) };
static AFTER: Cell<usize> = const { Cell::new(0) };
}
assert!(unsafe {
task_opt_callbacks(
|| Some(std::ptr::null()),
|p| {
DEPTH.set(DEPTH.get() + 1);
p
},
|_| DEPTH.set(DEPTH.get() - 1),
|_| 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, std::task::Poll::Ready(1));
assert_eq!(DEPTH.get(), 0);
}
#[test]
fn callbacks_set_once_and_wrap_spawned_tasks() {
use std::{cell::Cell, sync::atomic::AtomicUsize};
use crate::{System, testing::TestRunner};
const CHILD: &str = "NTEX_RT_TASK_CALLBACKS_CHILD";
static DATA: u8 = 0;
static CTX: u8 = 0;
static BEFORE: AtomicUsize = AtomicUsize::new(0);
static AFTER: AtomicUsize = AtomicUsize::new(0);
thread_local! {
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()
}
if std::env::var_os(CHILD).is_none() {
let out = std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"task::tests::callbacks_set_once_and_wrap_spawned_tasks",
])
.env(CHILD, "1")
.output()
.unwrap();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stdout)
);
return;
}
unsafe {
task_callbacks(
|| {
TRACK.get().then(|| {
BEFORE.fetch_add(1, Ordering::Relaxed);
data()
})
},
|p| {
assert_eq!(p, data());
INSIDE.set(true);
ctx()
},
|p| {
assert_eq!(p, ctx());
INSIDE.set(false);
},
|p| {
assert_eq!(p, data());
AFTER.fetch_add(1, Ordering::Relaxed);
},
);
}
unsafe {
task_callbacks(
|| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
);
assert!(!task_opt_callbacks(
|| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
|_| unreachable!(),
));
}
System::new("callbacks", TestRunner).block_on(async {
TRACK.set(true);
let hnd = crate::spawn(async { INSIDE.get() });
TRACK.set(false);
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);
TRACK.set(true);
let result = crate::spawn_sendable(async { INSIDE.get() });
TRACK.set(false);
assert_eq!(BEFORE.load(Ordering::Relaxed), 2);
assert!(result.await.unwrap());
assert!(!INSIDE.get());
assert_eq!(AFTER.load(Ordering::Relaxed), 2);
});
}