ChebbiOS

RTOS & Concurrency

Managed chaos: Mutexes, Semaphores, and ignoring interrupts safely.

Concurrency is hard. In embedded systems, it's deadly. You deal with shared resources (I2C bus, Memory) accessed by multiple tasks and interrupts.

Primitives

  • Mutex (Mutual Exclusion): Like a key to a bathroom. Only one task can hold it. Used to protect shared data. Has ownership (only the owner can unlock).
  • Semaphore: Like tickets to a movie. Can allow N tasks (Counting) or just signal an event (Binary). No ownership (ISR can release a semaphore waited on by a task).
  • Condition Variable: "Wake me up when X happens." Often used with a Mutex.
Mutex Scenario

"I am writing to the LCD screen. Don't interrupt me."

Semaphore Scenario

"ISR: Data arrived! Wake up the processing task."


C++ Implementation

Modern C++ provides safe wrappers for these primitives. Always prefer RAII wrappers like std::lock_guard over manual locking to prevent deadlocks.

Mutex (std::mutex)

C++11
#include <mutex>

std::mutex mtx; // Protects shared_data
int shared_data = 0;

void safe_increment() {
    // Constructor locks the mutex
    std::lock_guard<std::mutex> lock(mtx);
    
    // Critical Section
    shared_data++;
    
    // Destructor unlocks mutex automatically
    // (even if function returns or throws)
}

Semaphore (std::counting_semaphore)

C++20 Useful for Producer-Consumer patterns.
#include <semaphore>
#include <thread>

// Initialize with 0 resources
// Max count 10
std::counting_semaphore<10> signal(0); 

void producer() {
    // Prepare data...
    signal.release(); // Increment count (Signal)
}

void consumer() {
    signal.acquire(); // Decrement count (Wait)
    // Process data...
}

The Mars Pathfinder Problem

Priority Inversion is a classic deadlock scenario that almost killed the Mars Pathfinder mission.

  1. Low Priority task holds a Mutex.
  2. High Priority task needs the Mutex and blocks.
  3. Medium Priority task preempts the Low Priority task (because it doesn't need the Mutex).

Result: The High Priority task is blocked indefinitely by the Medium Priority task, even though they don't share resources!

Solution: Priority Inheritance. The OS temporarily boosts the Low Priority task to the level of the High Priority task, so it can finish its work and release the Mutex.

Read more about the first bug on Mars →

Connect & Discuss

Have questions about systems engineering, or found a bug in the code? Reach out!

Feedback

This blog is a static site, but I'd love to hear your thoughts. You can discuss this post by sending me an email or reaching out on social media.

Send Feedback