Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
reloadable_shell.hpp File Reference

A ready-made hot-reloadable plugin shell (DAW integration). More...

#include <pulp/format/processor.hpp>
#include <pulp/format/reload/build_fingerprint.hpp>
#include <pulp/format/reload/processor_hotswap_slot.hpp>
#include <pulp/format/reload/reload_abi.hpp>
#include <pulp/format/reload/reload_controller.hpp>
#include <pulp/format/reload/reload_trust_policy.hpp>
#include <pulp/format/reload/reload_library.hpp>
#include <pulp/format/reload/reload_transaction.hpp>
#include <pulp/runtime/log.hpp>
#include <pulp/state/store.hpp>
#include <pulp/view/view.hpp>
#include <algorithm>
#include <atomic>
#include <functional>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <system_error>
#include <thread>
#include <utility>
#include <vector>
#include <unistd.h>

Go to the source code of this file.

Classes

class  pulp::format::reload::ReloadableShell

Namespaces

namespace  pulp
namespace  pulp::format
 What the product grants a device: the quota table indexed by capability tier and live thermal state.
namespace  pulp::format::reload

Macros

#define PULP_RELOAD_DEV_WATCHER   1

Detailed Description

A ready-made hot-reloadable plugin shell (DAW integration).

This is the piece that makes DSP hot-reload work inside a real host (REAPER, Logic, a DAW): a Processor the format adapters (VST3 / AU / CLAP / Standalone) wrap exactly like any other, except its DSP lives in a separately-compiled "logic" shared library that can be recompiled and swapped while the host keeps the plugin instance — and the audio stream — alive.

Split of responsibilities:

  • The SHELL (this class) owns the audio entry point, the StateStore the host gave it, the RT-safe ProcessorHotSwapSlot, and a control-thread watcher.
  • The LOGIC library exports the reload ABI (PULP_RELOAD_LOGIC) and contains the actual DSP. The shell dlopens it, gates it (reload-ABI version, build fingerprint, parameter contract), and swaps the new Processor into the slot on success.

Threading:

  • process() runs on the audio thread and only ever calls slot_.process() (a non-blocking try-lock; passthrough on swap contention). It never loads, allocates, or destroys.
  • A single background WATCHER thread (started in prepare(), joined in release()/dtor) polls the logic file's mtime and performs the dlopen + gated swap. The slot's writer lock proves no audio reader is inside the old DSP before it is destroyed on this control thread. dlopen / file I/O / ~Processor() therefore never touch the audio thread.

The parameter contract is FIXED at load time: the shell mirrors the initial logic library's parameters into the host's store, and a reload whose contract differs is rejected (the sound keeps playing on the previous DSP). Changing the automatable parameter set still requires a full plugin reload — only the DSP behind a stable contract hot-swaps. This is by design (the host has already cached the parameter list).

Logic path resolution (in order): the explicit constructor argument, else the PULP_RELOAD_LOGIC_PATH environment variable, else the empty path (the shell runs as passthrough with no parameters and logs a clear diagnostic — e.g. the host scanned the plugin before the developer built the logic library).

Macro Definition Documentation

◆ PULP_RELOAD_DEV_WATCHER

#define PULP_RELOAD_DEV_WATCHER   1