Threads

A thread in Drone OS corresponds to a hardware interrupt. It is a sequence of fibers that managed independently by an interrupt controller. Threads can not be created on demand, but should be pre-defined for a particular project. Then any number of fibers can be attached dynamically to a particular thread.

Threads should be defined at src/thr.rs using thr::nvic! macro:


#![allow(unused)]
fn main() {
thr::nvic! {
    /// Thread-safe storage.
    thread => pub Thr {};

    /// Thread-local storage.
    local => pub ThrLocal {};

    /// Vector table.
    vtable => pub Vtable;

    /// Thread token set.
    index => pub Thrs;

    /// Threads initialization token.
    init => pub ThrsInit;

    threads => {
        exceptions => {
            /// All classes of faults.
            pub hard_fault;
        };
        interrupts => {
            /// A thread for my task.
            10: pub my_thread;
        };
    };
}
}

The macros will define THREADS static array of Thr objects. In this example the array will contain three element: HARD_FAULT, MY_THREAD, and the implicit RESET thread data. Thrs structure is also created here, which is a zero-sized type, a set of tokens, through which one can manipulate the threads. This set of token can be instantiated only once, usually at the very beginning of the root task:


#![allow(unused)]
fn main() {
/// The root task handler.
#[inline(never)]
pub fn handler(reg: Regs, thr_init: ThrsInit) {
    let thr = thr::init(thr_init);

    // ... The rest of the handler ...
}
}

Here the thr variable contains tokens for all defined threads. If you have added fields to the Thr definition, they are accessible through thr.my_thread.to_thr(). ThrLocal is also stored inside Thr, but accessible only through the Thr::local() associated function.

A thread can be called programmatically using implicit core::task::Waker or explicit thr.my_thread.trigger() or directly by hardware peripherals. If the thread, which was triggered, has a higher priority than the currently active thread, the active thread will be preempted. If the thread has a lower priority, it will run after all higher priority threads. Priorities can be changed on the fly with thr.my_thread.set_priority(...) method.

Fiber chain

The main thing a thread owns is a fiber chain. A fiber chain is essentially a linked list of fibers. A fiber can be added to a thread chain dynamically using thr.my_thread.add_fib(...), or other methods based on it. The add_fib method is atomic, i.e. fibers can be added to a particular thread from other threads.

When a thread is triggered, it runs the fibers in its fiber chain one-by-one in LIFO order. In other words the most recently added fiber will be executed first. A fiber can return fib::Yielded result, which means the fiber is paused but not completed; the thread will keep the fiber in place for the later run and proceed with the next fiber in the chain. Or the fiber can return fib::Complete, in which case the thread removes the fiber from the chain, runs its drop destructor, and proceeds to the next fiber in the chain.