Threads#

Threads Basics#

  • Primary advantage over interrupts: may block. E.g.

  • Zephyr

    • Preemptive: timesliced (much like SCHED_RR in Linux)

    • Cooperative: not timesliced (scheduling disabled); can still be interrupted by an ISR

  • One stack per thread

  • Priorities: when a higher priority thread becomes runnable, the currently running thread is suspended, and a context switch is made to run the higher priority thread

Main Thread#

  • Main thread: always there, implicitly created

  • Zephyr: runs at highest priority

  • ⟶ Careful

#include <why-thread.h>
#include <print>

int main()
{
    Why::init();

    auto mainthread = Why::Thread::self();
    std::println("priority: {}", mainthread.prioritystr());
    return 0;
}
$ sudo ./why-threads-mainthread
priority: Highest

Explicitly Created Threads#

  • Created explicitly to handle a specific task

  • Priority to distinguish from other threads

  • Zephyr: priority is mandatory

  • WhyOS: priority is optional; default Linux fair scheduling

#include <why-thread.h>
#include <print>
#include <cstdlib>

int main()
{
    Why::init();

    auto result = Why::Thread::create(
        [](){
            std::println("being scheduled, priority: {}", Why::Thread::self().prioritystr());
        }
    );
    if (!result) {
        std::println("no thread created: {}", result.error().msg());
        return 1;
    }
    return 0;
}
#include <why-thread.h>
#include <print>

int main()
{
    Why::init();

    auto result = Why::Thread::create(
        [](){
            std::println("being scheduled, priority: {}", Why::Thread::self().prioritystr());
        }, 
        Why::Thread::Priority::Medium
    );
    if (!result) {
        std::println("no thread created: {}", result.error().msg());
        return 1;
    }
    return 0;
}

Scheduling: Coordinating Multiple Threads#

  • A running thread is given only so much time (a timeslice); after that time a scheduling decision is made

  • Scheduling decision: choosing a runnable/ready thread to run on the CPU

  • Context switch: saving CPU registers of one thread, and restoring those of another

  • Potential reschedule points (i.e. when a scheduling decision is made)

    • A thread has consumed its timeslice ⟶ from potentially multiple runnable threads one must be chosen

    • A thread voluntarily relinquishes the CPU; for example by calling Why::msleep()

    • Return of an ISR

  • A thread becomes runnable/ready, when for example

    • awakes from Why::msleep()

    • a semaphore that it was waiting to take is given (by another thread or an interrupt)

  • In the moment a thread becomes runnable, and a lower priority thread has the CPU, a context switch is made in favor of the newcomer

  • Interrupts are not schedulable ⟶ are not scheduled

Attention: Blocking In Interrupt Service Routines#

  • Programming paradigm: waiting - voluntarily relinquishing the CPU

  • ISR must not wait - if they do …

    • ⟶ all sorts of undefined behavior (for performance reasons checks are not made)

    • Interrupts not arriving anymore

    • Stalling the system

  • WhyOS: interrupts are queued until the ISR returns ⟶ correct, but huge latency problem

  • Other archs/platforms may differ

Demo: Sleeping in an ISR#

  • Arch dependent

  • WhyOS: crash if blocking in syscall

#include <why-irq.h>
#include <why-time.h>
#include <print>

int main()
{
    Why::init();
    Why::IRQ::connect(
        5, 
        [](int){
            std::println("seeing irq 5, nap time ...");
            Why::msleep(5*1000);
            std::println("... wakeup");
        }
    );
    Why::pause();
    return 0;
}
$ why-shell ./why-threads-sleep-in-isr
> irq 5
seeing irq 5, nap time ...
> irq 5
... 5 seconds silence ...
> ... wakeup
seeing irq 5, nap time ...

Demo: Scheduling In Action#

#include <why-thread.h>
#include <why-time.h>
#include <print>

int main()
{
    Why::init();

    auto thread1 = Why::Thread::create(
        [](){
            for (;;)
                std::println("1");
        },
        Why::Thread::Priority::Medium
    );
    if (! thread1) {
        std::println(stderr, "thread creation failed: {}", thread1.error().msg());
        return 1;
    }
    auto thread2 = Why::Thread::create(
        [](){
            for (;;)
                std::println("2");
        },
        Why::Thread::Priority::Medium
    );
    if (! thread2) {
        std::println(stderr, "thread creation failed: {}", thread2.error().msg());
        return 1;
    }

    Why::pause();
    return 0;
}
$ sudo ./why-threads-scheduling-demo
1
2
1
1
2
...