Skip to content

Mutex can deadlock when used with multiple priority tasks #1204

Open
@pattop

Description

@pattop

Below is a test case demonstrating the issue.

I think what's happening is that the two high priority tasks are fighting over the mutex, preventing the low priority task from making forward progress so that it can release the mutex.

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use defmt_rtt as _;
use embassy_stm32::{
    executor::Executor, executor::InterruptExecutor, interrupt, interrupt::InterruptExt, time,
};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;

static MUTEX: Mutex<CriticalSectionRawMutex, u32> = Mutex::new(0);

#[cortex_m_rt::entry]
fn main() -> ! {
    /* clocks */
    let mut cfg = embassy_stm32::Config::default();
    cfg.rcc.hse = Some(time::mhz(8));
    cfg.rcc.sys_ck = Some(time::mhz(180));
    embassy_stm32::init(cfg);

    /* enable flash cache & prefetching */
    unsafe {
        embassy_stm32::pac::FLASH.acr().modify(|w| {
            w.set_dcen(true);
            w.set_icen(true);
            w.set_prften(true);
        });
    }

    /* high priority */
    let irq = interrupt::take!(UART4);
    irq.set_priority(interrupt::Priority::P14);
    static EXECUTOR_HIGH: StaticCell<InterruptExecutor<interrupt::UART4>> = StaticCell::new();
    let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq));
    let spawner = executor.start();
    defmt::unwrap!(spawner.spawn(high_task(1)));
    defmt::unwrap!(spawner.spawn(high_task(2)));

    /* low priority */
    static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
    let executor = EXECUTOR_LOW.init(Executor::new());
    executor.run(|spawner| {
        defmt::unwrap!(spawner.spawn(low_task()));
    });
}

#[embassy_executor::task]
async fn low_task() {
    loop {
        defmt::println!("low priority task locking mutex");
        let mut m = MUTEX.lock().await;
        defmt::println!("low priority task got mutex");
	for i in 0..10 {
		cortex_m::asm::delay(10000000); // ~100 millisecond
		defmt::println!("low priority task busy {}", i);
	}
	*m += 1;
        defmt::println!("low priority task releasing mutex");
    }
}

#[embassy_executor::task(pool_size=2)]
async fn high_task(i: usize) {
    defmt::println!("high priority task {} waiting", i);
    Timer::after(Duration::from_millis(500)).await;
    loop {
        defmt::println!("high priority task {} locking mutex", i);
        let mut m = MUTEX.lock().await;
        defmt::println!("high priority task {} got mutex", i);
        Timer::after(Duration::from_millis(500)).await;
	*m += 1;
        defmt::println!("high priority task {} releasing mutex", i);
    }
}

#[panic_handler]
fn panic(panic: &core::panic::PanicInfo) -> ! {
    defmt::error!("{}", defmt::Display2Format(panic));
    cortex_m::asm::udf();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions