Writing a lock-free ring buffer for the audio thread
Why the audio callback can never wait, and the 60 lines of C that keep it fed.
This is a placeholder post, here only to show how the site behaves with more writing in it. It will be removed. The rest of the text is filler, written to look like a real post so the layout has something realistic to hold.
The problem
Real-time audio has one rule above all the others: the callback must finish on time, every time. Miss the deadline and the listener hears it immediately, as a click or a dropout. That rule shapes everything else, from how memory is allocated to how threads talk to each other.
typedef struct {
float *data;
_Atomic uint32_t read, write;
uint32_t mask;
} Ring;
static bool ring_push(Ring *r, float v) {
uint32_t w = atomic_load_explicit(&r->write, memory_order_relaxed);
if (w - atomic_load_explicit(&r->read, memory_order_acquire) > r->mask) return false;
r->data[w & r->mask] = v;
atomic_store_explicit(&r->write, w + 1, memory_order_release);
return true;
}
What I learned
Graphics has a softer deadline but a wider surface. A frame that arrives late is a stutter rather than a click, but there are far more ways to be late: too many draw calls, too much overdraw, a texture upload at the wrong moment, or a shader that is quietly doing far more work than it needs to.
The interesting part is always the same. You start with a clean idea, you meet the hardware, and the idea changes shape. What survives is usually simpler than what you started with, and faster for it.