embedded-alloc icon indicating copy to clipboard operation
embedded-alloc copied to clipboard

safe example with spin

Open Dajamante opened this issue 2 years ago • 2 comments

We would like to add an example using spin to make sure the heap is initialized once :).

Dajamante avatar Oct 03 '23 13:10 Dajamante

@Dirbaio what do you propose instead for ensuring the heap is only initialised once?

thejpster avatar Nov 20 '23 07:11 thejpster

Using spin is always a bug in bare-metal.

For example, if you use it to sync data between main and interrupt (as a substitute of std::sync) you WILL get deadlocks if, while main holds the lock, an interrupt fires. The interrupt will spin waiting on the already-acquired lock, which should be released by main, but main will never contine running until the interrupt returns.

Same for all the other sync primitives spin has, including spin::Once.

We really shouldn't promote it in embedded contexts, or people will see "ooh, nostd mutex, shiny!" and use it and shoot themselves in the foot.

I agree. Spinning is an easy way to deadlock when there are interrupts handled. It is important to know if you are in cooperative or preemptive execution.

I use the heap with the nrf-softdevice, spawning cooperative executors from the preemptive executor after the heap is initialized. Just initialize the heap first thing when nothing is spawned that can allocate. Nothing is going to be allocating before you have an uncontested chance to initialize.

const HEAP_SIZE: usize = 192 * 1024;
#[repr(align(1024))]
struct HeapBuffer([u8; HEAP_SIZE]);
static mut HEAP_BUFFER: HeapBuffer = HeapBuffer([0; HEAP_SIZE]);
#[global_allocator]
static HEAP: Heap = Heap::empty();

///
/// SoftDevice gets 0,1,and 4 priorities
/// GPIO and Timer get 2 priorities
/// SAADC gets 3 priority
///
/// SoftDevice uses:
///
/// TIMER0
/// SWI1_EGU1 is priority 6 Radio Notification
/// SWI2_EGU2 is priority 6 SoftDevice Event Notification
/// SWI4_EGU4 is reserved for future use
/// SWI5_EGU5 is priority 4 SoftDevice Processing
/// PPI 20 and up ... and maybe more
/// check at https://infocenter.nordicsemi.com/pdf/S140_SDS_v2.1.pdf
///
///
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
    // Configure for low-power SoftDevice operation
    let mut config = embassy_nrf::config::Config::default();
    config.gpiote_interrupt_priority = Priority::P2;
    config.time_interrupt_priority = Priority::P2;
    config.lfclk_source = embassy_nrf::config::LfclkSource::ExternalXtal;
    let mut p = embassy_nrf::init(config);

    // Confirm to the bootloader that we are alive by consuming the watchdog
    let wdt_config = wdt::Config::try_new(&p.WDT).unwrap();
    let (_wdt, [wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) {
        Ok(x) => x,
        Err(_) => {
            // Watchdog already active with the wrong number of handles, waiting for it to timeout...
            loop {
                cortex_m::asm::wfe();
            }
        }
    };

    // Okay, we are booted.
    info!("Hello World!");

    // Before anything can attempt to allocate from the HEAP, initialize the heap memory
    // SAFETY: No tasks that can touch the heap may be spawned before this initialization
    unsafe {
        HEAP.init(HEAP_BUFFER.0.as_mut_ptr() as usize, HEAP_BUFFER.0.len());
    }
    
    // Configuration ...
    
    // High-priority executor: SWI0_EGU0
    info!("Starting SWI0 executor and task");
    interrupt::SWI0_EGU0.set_priority(Priority::P5);
    let swi_spawner = EXECUTOR_SWI0.start(interrupt::SWI0_EGU0);
    unwrap!(swi_spawner.spawn(run_watchdog(wdt_handle)));
    unwrap!(swi_spawner.spawn(run_ble()));

    // Medium-priority executor: SWI3_EGU3
    info!("Starting SWI3 executor and task");
    interrupt::SWI3_EGU3.set_priority(Priority::P6);
    let swi_spawner = EXECUTOR_SWI3.start(interrupt::SWI3_EGU3);
    unwrap!(swi_spawner.spawn(run_battery_check(saadc, p.P0_02.degrade())));
    unwrap!(swi_spawner.spawn(run_imu(imu_spim, imu_spi_cs, imu_spi_int)));
    unwrap!(swi_spawner.spawn(run_pdm(pdm)));

    // Run low priority, expensive/blocking work in thread mode
    unwrap!(spawner.spawn(run_cpu_imu()));
    unwrap!(spawner.spawn(run_cpu_pdm()));
    unwrap!(spawner.spawn(run_cpu_heartbeat()));
    unwrap!(spawner.spawn(run_cpu_temperature()));
    unwrap!(spawner.spawn(run_cpu_nand(nand_spim, nand_spi_cs, nand_wp, nand_hold)));
    unwrap!(spawner.spawn(run_dfu()));
   
    ...
    ```

trueb2 avatar Jan 29 '24 13:01 trueb2