Class thl::RCU#
-
template<typename T>
class RCU# Generic RCU (Read-Copy-Update) container for lock-free reads.
This template provides lock-free read access to any data structure while allowing safe updates through copy-on-write semantics. Perfect for real-time audio applications where readers (audio thread) must never block.
IMPORTANT: For real-time threads, call register_reader_thread() before entering the real-time context (e.g., during audio setup). This allocates the reader node outside the real-time path.
- Template Parameters:
T – The type of data to protect (e.g., std::map, std::vector)
Public Types
Public Functions
-
inline explicit RCU(const T &initial_data = T{}, size_t cleanup_threshold = 8, size_t emergency_threshold = 32)#
Construct RCU with initial data.
- Parameters:
initial_data – Initial value (will be copied)
cleanup_threshold – Number of retired versions before aggressive cleanup
emergency_threshold – Number of retired versions before blocking cleanup
-
inline explicit RCU(T &&initial_data, size_t cleanup_threshold = 8, size_t emergency_threshold = 32)#
Construct RCU with moved data.
- Parameters:
initial_data – Initial value (will be moved)
cleanup_threshold – Number of retired versions before aggressive cleanup
emergency_threshold – Number of retired versions before blocking cleanup
-
inline ~RCU()#
-
template<typename Func>
inline auto read(Func &&read_func) const -> decltype(read_func(std::declval<const T&>()))# Lock-free read access.
Provides safe read access to the data structure. The provided function is called with a const reference to the current data. The data is guaranteed to remain valid for the duration of the function call.
Usage:
rcu_map.read([&](const auto& map) { auto it = map.find("key"); if (it != map.end()) { value = it->second; } });
- Parameters:
read_func – Function to call with const reference to data
-
template<typename Func>
inline void update(Func &&update_func)# Update data using copy-on-write.
Creates a copy of the current data, allows modification via the provided function, then atomically publishes the new version. This operation may block and is not real-time safe.
Usage:
rcu_map.update([&](auto& map) { map["new_key"] = value; map.erase("old_key"); });
- Parameters:
update_func – Function to modify the copied data
-
template<typename Func>
inline void replace(Func &&build_func)# Publish a freshly built version, without reading the current one.
Same publication and reclamation as update(), but the new version is default-constructed and filled in by build_func rather than copied from the live one. Use this when the writer rebuilds T from scratch anyway — it skips a pointless deep copy, and, more importantly, it never reads the version the readers are using. That matters when readers mutate state inside T (RT scratch state reached through a const reference, for example): update()’s copy would race with those writes, this does not.
Requires T to be default-constructible. May block; not real-time safe.
Usage:
rcu_config.replace([&](auto& config) { config.entries = std::move(freshly_built_entries); });
- Parameters:
build_func – Function that fills in the fresh, empty value
-
inline void register_reader_thread() const#
Register the current thread for real-time safe reads.
Call this method from any thread that will use read() before entering a real-time context. This allocates the reader node, so subsequent read() calls are allocation-free and real-time safe.
Safe to call multiple times - subsequent calls are no-ops. NOT real-time safe (allocates memory on first call per thread).
Usage:
// During audio setup (non-RT context) - preferred rcu_state.register_reader_thread(); // Or just call read() - auto-registers on first use rcu_state.read([](const auto& data) { // First call may allocate, subsequent calls are RT-safe });
Note
If not called explicitly, read() will auto-register on first use. But this first call is NOT real-time safe.
-
inline unsigned int get_reader_count() const#
-
inline void synchronize()#
-
class ReadScope#
RAII scope that holds an RCU read section open.
Use when a caller needs to keep the read section open across multiple operations (e.g. wrapping a whole audio block rather than each per-sample access). Opens rcu_read_lock() in the ctor and closes it in the dtor, while exposing the current data pointer.
Non-copyable, non-movable. Must not outlive the RCU instance. Nested scopes on the same thread/instance are not supported.
Usage:
// dtor releases sectionauto scope = rcu.read_scope(); // auto-registers, enters section use(scope.data()); // data() valid until dtor
Public Functions
-
inline ~ReadScope()#
-
inline ~ReadScope()#