Skip to main content

BackgroundTask

Trait BackgroundTask 

Source
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§

Source

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§

Source

type Params: Params

The plugin’s parameter struct; must match the leaf trait’s type Params. (Send/'static on the task type itself: the pool moves it across threads and the worker outlives any block.)

Required Methods§

Source

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".

Implementors§