.. include:: Threads ======= .. topic:: Documentation * :doc:`../why-os/threads` * `Zephyr: Threads `__ * `Zephyr: Scheduling `__ .. topic:: See also * :doc:`/trainings/material/soup/linux/sysprog/posix-threads/index` Threads Basics -------------- * Primary advantage over interrupts: may *block*. E.g. * :doc:`mutex <../why-os/locking>` * :doc:`message queue <../why-os/communication>` * 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 * |longrightarrow| Careful .. literalinclude:: code/mainthread.cpp :caption: :download:`code/mainthread.cpp` :language: c++ .. code-block:: console $ 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* .. literalinclude:: code/explicit-noprio.cpp :caption: :download:`code/explicit-noprio.cpp` :language: c++ .. literalinclude:: code/explicit-prio.cpp :caption: :download:`code/explicit-prio.cpp` :language: c++ Scheduling: Coordinating Multiple Threads ----------------------------------------- .. topic:: Documentation * `Zephyr: Scheduling `__ * 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 |longrightarrow| 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 :doc:`semaphore <../semaphore/index>` 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 |longrightarrow| *are not scheduled* Attention: Blocking In Interrupt Service Routines ------------------------------------------------- * Programming paradigm: *waiting* - voluntarily relinquishing the CPU * ISR *must* not wait - if they do ... * |longrightarrow| 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 |longrightarrow| correct, but huge latency problem * Other archs/platforms may differ Demo: Sleeping in an ISR ------------------------ * Arch dependent * WhyOS: crash if blocking in syscall .. literalinclude:: code/sleep-in-isr.cpp :caption: :download:`code/sleep-in-isr.cpp` :language: c++ .. code-block:: console $ 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 -------------------------- .. literalinclude:: code/scheduling-demo.cpp :caption: :download:`code/scheduling-demo.cpp` :language: c++ .. code-block:: console $ sudo ./why-threads-scheduling-demo 1 2 1 1 2 ... .. .. jjj:: 9:30 17:30