Cortex M micros have a builtin preemptive async scheduler - the NVIC.
My personal style for doing embedded firmware have evolved to do everything in interrupt handlers and dynamically set priorities and pending bits to launch the next 'task' - main is just an empty for loop with a WFE/WFI instruction in it.
The hardware is even specifically designed for this style with interrupt call chaining - when you exit an interrupt handler and another pending interrupt is asserted you dont take the full overhead of pushing/popping registers.
My personal style for doing embedded firmware have evolved to do everything in interrupt handlers and dynamically set priorities and pending bits to launch the next 'task' - main is just an empty for loop with a WFE/WFI instruction in it. The hardware is even specifically designed for this style with interrupt call chaining - when you exit an interrupt handler and another pending interrupt is asserted you dont take the full overhead of pushing/popping registers.