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

using DataType = T#
using DataPtr = std::unique_ptr<T>#
using ReadFunction = std::function<void(const T&)>#
using UpdateFunction = std::function<void(T&)>#

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()#
RCU(const RCU&) = delete#
RCU &operator=(const RCU&) = delete#
RCU(RCU&&) = delete#
RCU &operator=(RCU&&) = delete#
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()#
inline ReadScope read_scope() const#

Open an RCU read section as an RAII scope.

See ReadScope.

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:

auto scope = rcu.read_scope();        // auto-registers, enters section
use(scope.data());                    // data() valid until dtor
// dtor releases section

Public Functions

inline explicit ReadScope(const RCU *rcu)#
inline ~ReadScope()#
ReadScope(const ReadScope&) = delete#
ReadScope &operator=(const ReadScope&) = delete#
ReadScope(ReadScope&&) = delete#
ReadScope &operator=(ReadScope&&) = delete#
inline const T &data() const#
inline const T *operator->() const#