Real-Time Safety#
The audio thread must never wait on anything with an unbounded worst case: no heap allocation, no mutex, no system call that can block, no I/O. tanh-lib encodes that as a handful of rules.
process() is non-blocking#
Every process() method, and every helper it reaches, is marked
TANH_NONBLOCKING_FUNCTION. The macro expands to [[clang::nonblocking]]
when the library is built with TANH_WITH_RTSAN and to nothing otherwise, so
the annotation documents the contract on every compiler and enforces it under
RTSan.
Register threads before touching State#
Any thread that reads thl::State or
thl::StateGroup from a real-time context calls
ensure_thread_registered() first. Registration allocates the per-thread RCU
slot once, up front, so the reads themselves stay lock- and allocation-free.
Numeric parameters are safe, strings may not be#
double, float, int and bool parameters are fully real-time
safe to read. String parameters are backed by std::string and may allocate
once a value grows beyond the small-string buffer; keep them off the audio
thread.
Logging from the audio thread#
thl::Logger::log / logf and friends format and dispatch synchronously
and are not real-time safe. From real-time code use
thl::Logger::rt::logf() or thl::Logger::rt::log(): the message is
formatted with thl::core::rt_snprintf (the allocation-free formatter), pushed
onto a lock-free queue and drained by a background thread into the regular
sinks.
Containers#
Buffer, MemoryBlock and RingBuffer allocate only in their
constructors and resize/prepare paths; the accessors used per sample are
allocation-free. An allocation that fails throws std::bad_alloc; a contract
violation such as an out-of-range index is an assert.
Checking it: RealtimeSanitizer#
Configure with -DTANH_WITH_RTSAN=ON (Clang 20 or newer) or use the
desktop-debug-rtsan preset. Every TANH_NONBLOCKING_FUNCTION then aborts
the test on the first allocation, lock, sleep or blocking syscall it reaches,
with a stack trace. The sanitizers workflow runs this leg in the merge
queue alongside ASan/UBSan, TSan and LSan.