Skip to content

Commit

Permalink
Replace nostd_async with a simple test executor
Browse files Browse the repository at this point in the history
  • Loading branch information
jannic committed Sep 8, 2024
1 parent 512b102 commit 0fcc24a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
2 changes: 1 addition & 1 deletion on-target-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ futures = {version = "0.3.30", default-features = false, features = ["async-awai
heapless = {version = "0.8.0", features = ["portable-atomic-critical-section", "defmt-03"]}
i2c-write-iter = {version = "1.0.0", features = ["async"]}
itertools = {version = "0.12.0", default-features = false}
nostd_async = {version = "0.6.1", features = ["wfe"]}
once_cell = { version = "1.19.0", default-features = false, features = ["critical-section"] }
panic-probe = {version = "0.3", features = ["print-defmt"]}
rp2040-boot2 = "0.3.0"
rp2040-hal = {path = "../rp2040-hal", features = ["critical-section-impl", "defmt", "rt", "i2c-write-iter"]}
Expand Down
1 change: 1 addition & 0 deletions on-target-tests/tests/i2c_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rp2040_hal::{

pub mod blocking;
pub mod non_blocking;
pub mod test_executor;

pub const ADDR_7BIT: u8 = 0x2c;
pub const ADDR_10BIT: u16 = 0x12c;
Expand Down
3 changes: 1 addition & 2 deletions on-target-tests/tests/i2c_tests/non_blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ pub struct State {
}

pub fn run_test(f: impl Future) {
let runtime = nostd_async::Runtime::new();
nostd_async::Task::new(f).spawn(&runtime).join();
super::test_executor::execute(f);
}
async fn wait_with(payload: &RefCell<TargetState>, mut f: impl FnMut(&TargetState) -> bool) {
while f(payload.borrow().deref()) {
Expand Down
78 changes: 78 additions & 0 deletions on-target-tests/tests/i2c_tests/test_executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! Simplistic test executor
//!
//! Compared to a real executor, this has some limitations:
//!
//! - Can only run to completion (like block_on, but without busy polling)
//! - Can't spawn additional tasks
//! - Must not be called multiple times concurrently
use core::{
future::Future,
pin::{self, Pin},
ptr::addr_of,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};

use once_cell::sync::OnceCell;

static WOKE: AtomicBool = AtomicBool::new(false);
static POLLING: AtomicBool = AtomicBool::new(false);

fn wake_fn(_data: *const ()) {
if !POLLING.load(Ordering::Relaxed) {
defmt::info!("waker called while not polling");
}
WOKE.store(true, Ordering::Relaxed);
}

const fn clone_fn(data: *const ()) -> RawWaker {
RawWaker::new(data, raw_waker_vtable())
}

fn drop_fn(_data: *const ()) {}

const fn raw_waker_vtable() -> &'static RawWakerVTable {
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone_fn, wake_fn, wake_fn, drop_fn);
&VTABLE
}

fn context() -> Context<'static> {
static DATA: () = ();

static WAKER: OnceCell<Waker> = OnceCell::new();
// Safety: The functions in the vtable of this executor only modify static atomics.
let waker = WAKER.get_or_init(|| unsafe { Waker::from_raw(clone_fn(addr_of!(DATA))) });

// Starting from rust 1.82.0, this could be used:
// static WAKER: Waker = unsafe { Waker::from_raw(clone_fn(addr_of!(DATA))) };

Context::from_waker(waker)
}

/// Run future to completion
///
/// poll() will only be called when the waker was invoked, so this is suitable to test
/// if the waker is properly triggered from an interrupt.
///
/// This won't work as expected of multiple calls to `execute` happen concurrently.
pub fn execute<T>(future: impl Future<Output = T>) -> T {
let mut pinned: Pin<&mut _> = pin::pin!(future);
if WOKE.load(Ordering::Relaxed) {
defmt::info!("woken before poll - ignoring");
}
POLLING.store(true, Ordering::Relaxed);
loop {
WOKE.store(false, Ordering::Relaxed);
if let Poll::Ready(result) = pinned.as_mut().poll(&mut context()) {
WOKE.store(false, Ordering::Relaxed);
POLLING.store(false, Ordering::Relaxed);
break result;
}
while !WOKE.load(Ordering::Relaxed) {
core::hint::spin_loop();
// TODO WFI or similar?
// But probably not important for this test executor
}
}
}

0 comments on commit 0fcc24a

Please sign in to comment.