#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
#![cfg_attr(feature = "nightly", feature(never_type))]
use std::boxed::Box;
use std::future::{Future, poll_fn};
use std::sync::{Arc, Mutex};
use std::task::Poll;
use embassy_executor::raw::Executor;
use embassy_executor::{Spawner, task};
#[unsafe(export_name = "__pender")]
fn __pender(context: *mut ()) {
unsafe {
let trace = &*(context as *const Trace);
trace.push("pend");
}
}
#[derive(Clone)]
struct Trace {
trace: Arc<Mutex<Vec<&'static str>>>,
}
impl Trace {
fn new() -> Self {
Self {
trace: Arc::new(Mutex::new(Vec::new())),
}
}
fn push(&self, value: &'static str) {
self.trace.lock().unwrap().push(value)
}
fn get(&self) -> Vec<&'static str> {
self.trace.lock().unwrap().clone()
}
}
fn setup() -> (&'static Executor, Trace) {
let trace = Trace::new();
let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
let executor = &*Box::leak(Box::new(Executor::new(context)));
(executor, trace)
}
#[test]
fn executor_noop() {
let (executor, trace) = setup();
unsafe { executor.poll() };
assert!(trace.get().is_empty())
}
#[test]
fn executor_task() {
#[task]
async fn task1(trace: Trace) {
trace.push("poll task1")
}
#[task]
async fn task2() -> ! {
panic!()
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone()).unwrap());
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "poll task1", ]
)
}
#[test]
fn executor_task_rpit() {
#[task]
fn task1(trace: Trace) -> impl Future<Output = ()> {
async move { trace.push("poll task1") }
}
#[cfg(feature = "nightly")]
#[task]
fn task2() -> impl Future<Output = !> {
async { panic!() }
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone()).unwrap());
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "poll task1", ]
)
}
#[test]
fn executor_task_self_wake() {
#[task]
async fn task1(trace: Trace) {
poll_fn(|cx| {
trace.push("poll task1");
cx.waker().wake_by_ref();
Poll::Pending
})
.await
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone()).unwrap());
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "poll task1", "pend", "poll task1", "pend", ]
)
}
#[test]
fn executor_task_self_wake_twice() {
#[task]
async fn task1(trace: Trace) {
poll_fn(|cx| {
trace.push("poll task1");
cx.waker().wake_by_ref();
trace.push("poll task1 wake 2");
cx.waker().wake_by_ref();
Poll::Pending
})
.await
}
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone()).unwrap());
unsafe { executor.poll() };
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "poll task1", "pend", "poll task1 wake 2", "poll task1", "pend", "poll task1 wake 2", ]
)
}
#[test]
fn waking_after_completion_does_not_poll() {
use embassy_sync::waitqueue::AtomicWaker;
#[task]
async fn task1(trace: Trace, waker: &'static AtomicWaker) {
poll_fn(|cx| {
trace.push("poll task1");
waker.register(cx.waker());
Poll::Ready(())
})
.await
}
let waker = Box::leak(Box::new(AtomicWaker::new()));
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone(), waker).unwrap());
unsafe { executor.poll() };
waker.wake();
unsafe { executor.poll() };
waker.wake();
waker.wake();
unsafe { executor.poll() };
executor.spawner().spawn(task1(trace.clone(), waker).unwrap());
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "poll task1", "pend", "pend", "pend", "poll task1", ]
)
}
#[test]
fn waking_with_old_waker_after_respawn() {
use embassy_sync::waitqueue::AtomicWaker;
async fn yield_now(trace: Trace) {
let mut yielded = false;
poll_fn(|cx| {
if yielded {
Poll::Ready(())
} else {
trace.push("yield_now");
yielded = true;
cx.waker().wake_by_ref();
Poll::Pending
}
})
.await
}
#[task]
async fn task1(trace: Trace, waker: &'static AtomicWaker) {
yield_now(trace.clone()).await;
poll_fn(|cx| {
trace.push("poll task1");
waker.register(cx.waker());
Poll::Ready(())
})
.await;
}
let waker = Box::leak(Box::new(AtomicWaker::new()));
let (executor, trace) = setup();
executor.spawner().spawn(task1(trace.clone(), waker).unwrap());
unsafe { executor.poll() };
unsafe { executor.poll() }; waker.wake();
unsafe { executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "yield_now", "pend", "poll task1", "pend", ]
);
let (other_executor, other_trace) = setup();
other_executor
.spawner()
.spawn(task1(other_trace.clone(), waker).unwrap());
unsafe { other_executor.poll() }; waker.wake(); unsafe { executor.poll() };
unsafe { other_executor.poll() };
assert_eq!(
trace.get(),
&[
"pend", "yield_now", "pend", "poll task1", "pend", ]
);
assert_eq!(
other_trace.get(),
&[
"pend", "yield_now", "pend", "poll task1", ]
);
}
#[test]
fn executor_task_cfg_args() {
#[task]
async fn task1(a: u32, b: u32, #[cfg(any())] c: u32) {
let (_, _) = (a, b);
}
#[task]
async fn task2(a: u32, b: u32, #[cfg(all())] c: u32) {
let (_, _, _) = (a, b, c);
}
}
#[test]
fn recursive_task() {
#[embassy_executor::task(pool_size = 2)]
async fn task1() {
let spawner = unsafe { Spawner::for_current_executor().await };
spawner.spawn(task1().unwrap());
}
}
#[cfg(feature = "metadata-name")]
#[test]
fn task_metadata() {
#[task]
async fn task1(expected_name: Option<&'static str>) {
use embassy_executor::Metadata;
assert_eq!(Metadata::for_current_task().await.name(), expected_name);
}
let (executor, _) = setup();
executor.spawner().spawn(task1(None).unwrap());
unsafe { executor.poll() };
let token = task1(Some("foo")).unwrap();
token.metadata().set_name("foo");
executor.spawner().spawn(token);
unsafe { executor.poll() };
let token = task1(Some("bar")).unwrap();
token.metadata().set_name("bar");
executor.spawner().spawn(token);
unsafe { executor.poll() };
let (executor, _) = setup();
executor.spawner().spawn(task1(None).unwrap());
unsafe { executor.poll() };
}