Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 350 of 9365. Communication Protocols and Data TransferFree lesson

Designing Circular Buffers for Data Streams

Construct thread-safe circular queue structures to decouple high-speed hardware data reception from slower application-level processing tasks.

In this lesson, you will learn how to make a circular buffer "thread-safe" so your high-speed hardware interrupts can safely hand off data to your slow-moving application tasks without losing bytes.

The Concurrency Problem

When you receive data via UART or DMA, the hardware often fires an interrupt service routine (ISR) to store the incoming byte. Meanwhile, your main application loop is busy processing that data. If your application reads from the buffer at the same time the ISR tries to write to it, you risk a race condition—where the pointers get updated inconsistently, leading to data corruption or lost stream integrity.

Implementing Thread-Safety

To decouple these tasks safely, we must ensure that the "Producer" (the ISR) and the "Consumer" (the application) cannot interrupt each other while modifying the buffer’s state. We achieve this by using a "Critical Section."

In a simple implementation, you disable interrupts globally for the few clock cycles required to update the buffer pointers. This ensures that the state of your head and tail pointers remains atomic.

// Simplified Thread-Safe Circular Buffer Access
uint8_t read_byte(void) {
    uint8_t data;
    
    // Enter Critical Section: Prevent ISR from firing
    __disable_irq(); 
    
    data = buffer[tail];
    tail = (tail + 1) % BUFFER_SIZE;
    
    // Exit Critical Section: Re-enable interrupts
    __enable_irq(); 
    
    return data;
}

Why This Decouples Processing

By wrapping pointer updates in a critical section, you create a robust "fire-and-forget" mechanism for your hardware. The hardware ISR fills the buffer whenever data arrives, and the application drains the buffer whenever it has the processing power. Because the critical section is extremely short, you don't stall the system or cause latency spikes for your high-speed peripheral.

This design allows the buffer to act as a temporal shock absorber. If the application is momentarily busy (e.g., performing a complex calculation or writing to a file), the hardware continues to deposit data into the circular queue. Once the application completes its task, it simply catches up by draining the accumulated bytes in the buffer, all without ever dropping a sample or creating a race condition.

Key Takeaway: Thread-safety in circular buffers is achieved by using critical sections to make pointer updates atomic, allowing hardware producers and application consumers to operate independently without data corruption.