In this fourth lesson in the Real-Time Operating System (RTOS) series, you’ll see how to replace horribly inefficient polling delay with efficient thread blocking.
In this fourth lesson in the Real-Time Operating System (RTOS) series, you’ll see how to replace the horribly inefficient polling delay with efficient blocking threads. Specifically, in this lesson 25, you’ll add a blocking OS_delay() function to the MiROS RTOS [1] and explore the far-reaching implications of thread blocking on the RTOS design.
Lesson 25 – RTOS Part-4: Efficient blocking of threads
Efficient Blocking by Switching Context
The general idea for avoiding wasteful polling, as in the BSP_delay() function, is to switch the CPU context away from the waiting thread and then switch the context back after the delay has elapsed. Such a waiting mechanism is called blockingand its primary benefit is the much more efficient use of the CPU because a blocked thread consumes no CPU cycles at all. Instead, the CPU can run other threads that actually have something useful to do.
From the application developer’s point of view, the behavior remains the same—the OS_delay() function does not return until the requested delay has elapsed. However, the OS_delay() implementation causes an upheaval in the internal RTOS design.
Threads States
One immediate consequence of the blocking mechanism is that the RTOS must “know” to exclude all blocked threads from the scheduling, meaning that threads need a new state “blocked” alongside the already existing state “ready.”
A very effective way to represent a dynamic life cycle of objects, like RTOS threads, is through a state machine. In the state diagram shown in the video, states are depicted as “blobs,” and state changes, called transitions, as “arrows.” The benefit of such a representation is that it forces you to consider all possible states and transitions. For example, a call to OS_delay() causes a transition to the “blocked” state. But you can also clearly see that another RTOS service is needed for transition out of the “blocked” state. That service, identified as OS_tick(), must be invoked from an interrupt (SysTick in the video) because the blocked thread is unresponsive and cannot do anything.
The Idle Thread
A state machine representation of the thread life cycle also provokes questions like: Can all threads simultaneously be in the “blocked” state? It turns out they can, but what should the scheduler do when all threads are excluded from scheduling?
The solution is to add a special idle threads, which the CPU can switch to when all other threads are blocked. This idle thread cannot block, so its life cycle has no “blocked” state. The main use of the idle state is to put the CPU and peripherals into a low-power sleep mode to conserve energy (eg, in battery-powered devices). In fact, providing the idle thread as a single, centralized place to perform power management is one of the RTOS’s significant benefits.
End Notes
Even though you’ve implemented thread blocking only for the time delay in this lesson, the blocking mechanism is fundamental to all other RTOS services for communication and synchronization among threads.
At this point, your MiROS RTOS [1] implements a round-robin scheduler with blocking, which roughly corresponds to the state-of-the-art time-sharing systems of the early 1960s. In the next lesson, you’ll finally learn what the phrase “Real-Time” in the name RTOS means, and you’ll bring the MiROS RTOS into the 1970s by implementing preemptive priority-based scheduling. Stay tuned!
[1] Miro Samek, MiROS (Minimal Real-Time Operating System)GitHub
Related Contents: