## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# Monotonic & spawn_

The understanding of time is an important concept in embedded systems, and to be able to run tasks based on time is essential. The framework provides the static methods `task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. `spawn_after` is more commonly used, but in cases where it's needed to have spawns happen without drift or to a fixed baseline `spawn_at` is available.

The `#[monotonic]` attribute, applied to a type alias definition, exists to support this. This type alias must point to a type which implements the `rtic_monotonic::Monotonic` trait. This is generally some timer which handles the timing of the system. One or more monotonics can coexist in the same system, for example a slow timer that wakes the system from sleep and another which purpose is for fine grained scheduling while the system is awake.

The attribute has one required parameter and two optional parameters, `binds`, `default` and `priority` respectively. The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's interrupt, while `default = true` enables a shorthand API when spawning and accessing time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority of the interrupt vector.

> The default `priority` is the maximum priority of the system. If your system has a high priority task with tight scheduling requirements, it might be desirable to demote the `monotonic` task to a lower priority to reduce scheduling jitter for the high priority task. This however might introduce jitter and delays into scheduling via the `monotonic`, making it a trade-off.

The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple. This activates the monotonics making it possible to use them.

See the following example:

    #![allow(unused)]
    fn main() {
    //! examples/schedule.rs

    #![deny(unsafe_code)]
    #![deny(warnings)]
    #![deny(missing_docs)]
    #![no_main]
    #![no_std]

    use panic_semihosting as _;

    #[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
    mod app {
        use cortex_m_semihosting::{debug, hprintln};
        use systick_monotonic::*;

        #[monotonic(binds = SysTick, default = true)]
        type MyMono = Systick<100>; // 100 Hz / 10 ms granularity

        #[shared]
        struct Shared {}

        #[local]
        struct Local {}

        #[init]
        fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
            let systick = cx.core.SYST;

            // Initialize the monotonic (SysTick rate in QEMU is 12 MHz)
            let mono = Systick::new(systick, 12_000_000);

            hprintln!("init");

            // Schedule `foo` to run 1 second in the future
            foo::spawn_after(1.secs()).unwrap();

            (
                Shared {},
                Local {},
                init::Monotonics(mono), // Give the monotonic to RTIC
            )
        }

        #[task]
        fn foo(_: foo::Context) {
            hprintln!("foo");

            // Schedule `bar` to run 2 seconds in the future (1 second after foo runs)
            bar::spawn_after(1.secs()).unwrap();
        }

        #[task]
        fn bar(_: bar::Context) {
            hprintln!("bar");

            // Schedule `baz` to run 1 seconds from now, but with a specific time instant.
            baz::spawn_at(monotonics::now() + 1.secs()).unwrap();
        }

        #[task]
        fn baz(_: baz::Context) {
            hprintln!("baz");
            debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
        }
    }
    }

    $ cargo run --target thumbv7m-none-eabi --example schedule
    init
    foo
    bar
    baz

A key requirement of a Monotonic is that it must deal gracefully with hardware timer overruns.

## Canceling or rescheduling a scheduled task

Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`, which allows canceling or rescheduling of the task scheduled to run in the future.

If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was too late and that the task is already sent for execution. The following example shows this in action:

    #![allow(unused)]
    fn main() {
    //! examples/cancel-reschedule.rs

    #![deny(unsafe_code)]
    #![deny(warnings)]
    #![deny(missing_docs)]
    #![no_main]
    #![no_std]

    use panic_semihosting as _;

    #[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
    mod app {
        use cortex_m_semihosting::{debug, hprintln};
        use systick_monotonic::*;

        #[monotonic(binds = SysTick, default = true)]
        type MyMono = Systick<100>; // 100 Hz / 10 ms granularity

        #[shared]
        struct Shared {}

        #[local]
        struct Local {}

        #[init]
        fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
            let systick = cx.core.SYST;

            // Initialize the monotonic (SysTick rate in QEMU is 12 MHz)
            let mono = Systick::new(systick, 12_000_000);

            hprintln!("init");

            // Schedule `foo` to run 1 second in the future
            foo::spawn_after(1.secs()).unwrap();

            (
                Shared {},
                Local {},
                init::Monotonics(mono), // Give the monotonic to RTIC
            )
        }

        #[task]
        fn foo(_: foo::Context) {
            hprintln!("foo");

            // Schedule `bar` to run 2 seconds in the future (1 second after foo runs)
            let spawn_handle = baz::spawn_after(2.secs()).unwrap();
            bar::spawn_after(1.secs(), spawn_handle, false).unwrap(); // Change to true
        }

        #[task]
        fn bar(_: bar::Context, baz_handle: baz::SpawnHandle, do_reschedule: bool) {
            hprintln!("bar");

            if do_reschedule {
                // Reschedule baz 2 seconds from now, instead of the original 1 second
                // from now.
                baz_handle.reschedule_after(2.secs()).unwrap();
                // Or baz_handle.reschedule_at(/* time */)
            } else {
                // Or cancel it
                baz_handle.cancel().unwrap();
                debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
            }
        }

        #[task]
        fn baz(_: baz::Context) {
            hprintln!("baz");
            debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
        }
    }
    }

    $ cargo run --target thumbv7m-none-eabi --example cancel-reschedule
    init
    foo
    bar