Threads and real time
A SHUFFLE application has three kinds of threads. The cores never create threads; they are given them through ports.
| Thread | Owned by | What runs there |
|---|---|---|
| control | the backend’s message loop, reached through the TaskQueue port |
every state change, commands, timers, UI updates, persistence requests |
| audio (and real-time processing threads) | the audio device or the engine | noexcept SHUFFLE_RT processing functions, nothing else |
| workers | the WorkerPool port |
file loading and writing, scans, analysis, network |
sequenceDiagram
participant C as control thread
participant A as audio thread
participant W as worker
C->>W: load a song (WorkerPool)
W-->>C: done (posted to the TaskQueue)
C->>A: new program, through a lock-free FIFO or an atomic swap
A-->>C: levels, positions (atomics), events (FIFO drained on a timer)
C->>C: old program destroyed here, never on the audio thread
Why the control thread is a port
Section titled “Why the control thread is a port”A core asks TaskQueue::isControlThread() and posts work with post(); it
never assumes which concrete thread that is. The same core therefore runs
under JUCE’s message thread, under your own loop (the miniaudio example runs
posted tasks from main), or under a test that calls
FakeTaskQueue::runPending() when it wants time to move.
Why the audio thread is special
Section titled “Why the audio thread is special”The audio thread must deliver a block before the device needs it, every few milliseconds, on every platform. Anything that can wait (allocation, a lock, I/O, an exception) can miss that deadline. So:
- functions that may run there are
noexceptand markedSHUFFLE_RT; - with Clang 20 and
SHUFFLE_ENGINE_RT_CHECKS=ON, the compiler rejects a marked function that allocates, locks, throws or calls unmarked code; - RealtimeSanitizer checks the same functions while tests run;
- data reaches the audio thread prepared in advance, through lock-free structures with fixed capacity.
Ports cross the boundary per block or per message, never per sample, so the inner loops stay plain code the compiler can optimise.
No blocking anywhere
Section titled “No blocking anywhere”The control thread never waits either: opening a device, scanning plugins and loading a song all answer later through a listener or a posted completion. Mobile systems require it, and it keeps a UI responsive on every platform.
How to apply this in your code: Respect the real-time rules. More: architecture.md.
© 2026 Umberto Sorbo. The software is licensed under the Apache License, Version 2.0, and provided “as is”, without warranties or conditions of any kind; see the disclaimer.
SHUFFLE Framework and the SHUFFLE Framework logo are trademarks of Umberto Sorbo; the licence grants no rights to them (trademarks). Other names belong to their owners. Fonts: IBM Plex Sans and JetBrains Mono, SIL Open Font License 1.1.
Contact: shuffleframework@gmail.com · Instagram @shuffleframework. Security problems: write to the same address with “Security” in the subject, not in a public issue.