Files
soundcraft/crates/vst3-host/tests/module_exit_live_instance.rs
ebbicottandClaude Opus 5.5 527f555512 Fix the crash on quit after a VST3 scan: run module exits on the entry thread
vst3-host ran each module's entry (bundleEntry / InitDll / ModuleEntry) when it
loaded a .vst3 and cached the module for the life of the process, but never ran
the matching exit or released the cached factory. At exit() the module's static
destructors then ran on a module that was never shut down. Steinberg's HALion
Sonic segfaults there, so every process that had scanned VST3 plugins crashed on
exit: the app on quit, soundcraft-cli, and the engine, mix and vst3-host unit
tests.

At process exit each module now has its factory released and its exit run,
newest first, on the thread that ran its entry: Native Instruments' Massive X
crashes in bundleExit on any other thread. Loads asked for off the main thread
go to a long-lived module thread, which also runs their exits. Modules with a
live instance or a leaked editor view are left alone, later loads are refused,
and plugin code stays mapped. The exit handler is registered with atexit after
each new module, so it runs ahead of that module's static destructors; it uses
OS thread ids because std::thread::current() panics in exit handlers. A module
whose entry ran but that then fails to load now gets its exit too.

The fixture plugin now checks the module lifecycle and aborts the process when
it is broken. Three harness = false tests cover the exit after use, a live
instance at exit, and a load from a short-lived thread.

Fixes #16

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:45:32 -04:00

15 lines
730 B
Rust

//! A module with a plugin instance still alive at process exit keeps running: its module exit
//! would pull the module out from under that instance (`harness = false`, see `module_exit.rs`).
//! The fixture aborts the process if its module exit runs while any of its objects are alive.
mod common;
fn main() {
let dir = common::fixture_dir();
let gain = soundcraft_vst3_host::scan_paths(&[dir.to_path_buf()]).into_iter().find(|d| d.name == "Fixture Gain").expect("fixture").id;
let p = soundcraft_vst3_host::instantiate_plugin(&gain).expect("instantiate");
// Still alive at exit, like a plugin held by a mixer that is never torn down.
std::mem::forget(p);
println!("module_exit_live_instance: ok");
}