use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
use runite::channel::oneshot;
use runite::net::{TcpListener, TcpStream};
use runite::time::sleep;
use runite::{queue_macrotask, queue_microtask, run, spawn, yield_now};
type Order = Rc<RefCell<Vec<&'static str>>>;
fn record_order(build: impl FnOnce(&Order) + Send + 'static) -> Vec<&'static str> {
std::thread::spawn(move || {
let order: Order = Rc::new(RefCell::new(Vec::new()));
build(&order);
run();
Rc::try_unwrap(order)
.expect("all tasks should have completed and dropped their handles")
.into_inner()
})
.join()
.expect("runtime thread panicked")
}
async fn connected_pair() -> (TcpStream, TcpStream) {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let addr = listener.local_addr().expect("local_addr");
let accept = spawn(async move { listener.accept().await.expect("accept").0 });
let client = TcpStream::connect(addr).await.expect("connect");
let server = accept.await.expect("join accept task");
(server, client)
}
fn spawn_byte_writer(mut client: TcpStream) {
spawn(async move {
client.write_all(b"x").await.expect("write pending byte");
});
}
#[test]
fn microtask_drains_before_macrotask() {
let order = record_order(|order| {
{
let order = Rc::clone(order);
queue_macrotask(move || order.borrow_mut().push("macrotask"));
}
{
let order = Rc::clone(order);
queue_microtask(move || order.borrow_mut().push("microtask"));
}
});
assert_eq!(order.as_slice(), ["microtask", "macrotask"]);
}
#[test]
fn yield_now_is_a_microtask_and_beats_a_pending_macrotask() {
let order = record_order(|order| {
let order = Rc::clone(order);
spawn(async move {
{
let order = Rc::clone(&order);
queue_macrotask(move || order.borrow_mut().push("macrotask"));
}
yield_now().await;
order.borrow_mut().push("after_yield");
});
});
assert_eq!(order.as_slice(), ["after_yield", "macrotask"]);
}
#[test]
fn inprocess_channel_wake_is_a_microtask_and_beats_a_pending_macrotask() {
let order = record_order(|order| {
let (tx, mut rx) = oneshot::channel::<()>();
{
let order = Rc::clone(order);
spawn(async move {
let _ = rx.recv().await;
order.borrow_mut().push("after_recv");
});
}
{
let order = Rc::clone(order);
spawn(async move {
{
let order = Rc::clone(&order);
queue_macrotask(move || order.borrow_mut().push("macrotask"));
}
let _ = tx.send(());
});
}
});
assert_eq!(order.as_slice(), ["after_recv", "macrotask"]);
}
#[test]
fn sleep_completion_takes_a_macro_turn() {
let order = record_order(|order| {
{
let order = Rc::clone(order);
queue_macrotask(move || order.borrow_mut().push("macrotask"));
}
{
let order = Rc::clone(order);
spawn(async move {
sleep(Duration::ZERO).await;
order.borrow_mut().push("after_sleep");
});
}
});
assert_eq!(order.as_slice(), ["macrotask", "after_sleep"]);
}
#[test]
fn io_completion_takes_a_macro_turn() {
let order = record_order(|order| {
let order = Rc::clone(order);
spawn(async move {
let (mut server, client) = connected_pair().await;
spawn_byte_writer(client);
{
let order = Rc::clone(&order);
queue_macrotask(move || order.borrow_mut().push("macrotask"));
}
let mut buf = [0u8; 1];
server
.read_exact(&mut buf)
.await
.expect("read pending byte");
order.borrow_mut().push("after_io");
});
});
assert_eq!(order.as_slice(), ["macrotask", "after_io"]);
}
#[test]
fn io_completion_does_not_preempt_a_microtask_chain() {
const CHAIN: usize = 16;
const LABELS: [&str; CHAIN] = [
"m0", "m1", "m2", "m3", "m4", "m5", "m6", "m7", "m8", "m9", "m10", "m11", "m12", "m13",
"m14", "m15",
];
fn pump(order: Order, next: usize) {
if next >= CHAIN {
return;
}
order.borrow_mut().push(LABELS[next]);
let order = Rc::clone(&order);
queue_microtask(move || pump(order, next + 1));
}
let order = record_order(|order| {
let order = Rc::clone(order);
spawn(async move {
let (mut server, client) = connected_pair().await;
spawn_byte_writer(client);
pump(Rc::clone(&order), 0);
let mut buf = [0u8; 1];
server
.read_exact(&mut buf)
.await
.expect("read pending byte");
order.borrow_mut().push("after_io");
});
});
let mut expected: Vec<&'static str> = LABELS.to_vec();
expected.push("after_io");
assert_eq!(order, expected);
}