embassy icon indicating copy to clipboard operation
embassy copied to clipboard

To much yield_now() can panic!

Open ra1u opened this issue 2 years ago • 2 comments

It seems that yield_now() can induce panic.

What happens is if we yield_now() in main() twice, we can generate panic.

This issue applies also on embedded systems / stm32.

To reproduce copy into embassy/examples/std/src/bin/tick.rs and run RUST_LOG=info cargo run --bin tick --release

#![feature(type_alias_impl_trait)]

use embassy_executor::executor::Spawner;
use embassy_util::channel::signal::Signal;
use embassy_util::yield_now;
use log::*;

static SIGNAL: Signal<u32> = Signal::new();

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    env_logger::builder()
        .filter_level(log::LevelFilter::Debug)
        .format_timestamp_nanos()
        .init();

    spawner.spawn(task1()).unwrap();
    spawner.spawn(task2()).unwrap();
    yield_now().await;
    // comment next line to not panic
    yield_now().await;
    SIGNAL.signal(1);
}

#[embassy_executor::task]
async fn task1() -> ! {
    task_common(1).await
}

#[embassy_executor::task]
async fn task2() -> ! {
    task_common(2).await
}

async fn task_common(id: i32) -> ! {
    let mut i = 0;
    let step = 1000000;
    loop {
        let x = SIGNAL.wait().await;
        if x >= i {
            info!("signal {} received {}", id, x);
            i += step;
        }
        SIGNAL.signal(x + 1);
        yield_now().await;
    }
}

ra1u avatar Aug 06 '22 09:08 ra1u

This is due to doing .wait() on the same signal from 2 tasks at the same time.

There's a pending PR to switch Signal to use the waitqueue utils (#498). When that's done it won't panic, but the signal will only send the value to one task, not both, which is probably not what you want.

If you want "broadcast" capabilities, you can use pubsub. Alternatively, if you don't need async-waiting, a static with a Mutex will be simpler to share state between tasks.

Dirbaio avatar Aug 06 '22 11:08 Dirbaio

Thank You now it works.

Issue with server I was building, boiled down to this, and now when I am running with mutex instead of signal it works ok. Regarding documentation on signal, my understanding was that there is only one who receives signal, just like proposed PR. https://docs.embassy.dev/embassy/git/std/channel/signal/struct.Signal.html

Allows creating awaitable signals that may be passed between tasks. For a simple use-case where the receiver is only ever interested in the latest value of something, Signals work well.

Thank you for support and this work.

ra1u avatar Aug 06 '22 12:08 ra1u

Fixed in #946

Dirbaio avatar Oct 04 '22 11:10 Dirbaio