pub trait BackgroundTask: Send + 'static {
type Params: Params;
const SERIALIZED: bool = false;
// Required method
fn run(self, params: &Self::Params);
}Expand description
Opt-in managed background work. Each task type implements this to
declare its params, its concurrency mode, and the handler the framework
runs on a shared background-thread pool; a plugin lists one or more task
types with the tasks: key on truce::plugin! (tasks: [Rebuild, Analyze]). Every task type gets its own inbound queue and mode, so a
serialized lane and a concurrent lane coexist in one plugin. Nothing
changes for a plugin that declares no tasks.
run executes off the audio thread and reaches shared state through
params (its #[skip] channels / atomics), exactly like the editor:
it must never touch DspState, which is audio-thread-exclusive.
Feedback to the audio thread stays the plugin’s job through those
#[skip] channels.
Keep handlers short and non-blocking. The pool is shared by every
truce plugin in the host and small (available_parallelism() - 1
threads, as few as one), so a handler that blocks on I/O (reading a
sample off disk) or waits on a lock stalls background work for every
other instance too, not just its own. Allocation and CPU-bound bursts
are fine - that is what the pool is for. For work that genuinely blocks
or runs long, give the plugin its own thread with
AudioTap::spawn_worker rather than the shared pool.
Schedule tasks with ctx.tasks::<Rebuild>() from process (wait-free),
the editor’s PluginContext, or the InitContext passed to init -
the type parameter selects the lane.
struct Rebuild { sample_rate: f64, time_s: f32 }
impl BackgroundTask for Rebuild {
type Params = ReverbParams;
const SERIALIZED: bool = true; // non-reentrant graph build
fn run(self, params: &ReverbParams) {
let graph = build_graph(self.sample_rate, self.time_s);
let _ = params.ready.force_push(graph); // #[skip] handoff
}
}
// truce::plugin! { logic, params, tasks: [Rebuild] }Provided Associated Constants§
Sourceconst SERIALIZED: bool = false
const SERIALIZED: bool = false
Run this lane’s handler one at a time for a given instance (“one-slot” mode).
Default false: the pool is shared and lock-free, so a burst that
re-arms a lane while a worker is still draining it can hand a second
worker the same lane - run may run concurrently with itself
for one instance. A handler that only talks to the audio thread
through lock-free channels / atomics (the reverb example’s MPMC
handoff) is fine that way and keeps maximum throughput.
Set true when the handler read-modify-writes shared mutable state
that isn’t safe to enter re-entrantly (a scratch buffer, a
non-atomic cache): the pool then serializes this lane’s drains so at
most one run for this instance runs at a time, without the author
needing a try_lock guard. Tasks are never dropped or reordered;
serialization only bounds concurrency, so keep the handler short
(a long serialized handler delays this lane’s later tasks). The mode
is per lane, so a concurrent lane in the same plugin is unaffected.
Required Associated Types§
Required Methods§
Sourcefn run(self, params: &Self::Params)
fn run(self, params: &Self::Params)
Run one task on the pool. See the trait docs for the contract,
including the concurrency note on Self::SERIALIZED.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".