Files
soundcraft/crates/vst3-host/tests/module_exit_worker_thread.rs
T
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

18 lines
799 B
Rust

//! A module first loaded from a short-lived thread is entered and, at process exit, exited on
//! one thread that outlives it (`harness = false`, see `module_exit.rs`): the asking thread is
//! gone by then, and modules tie state to the thread of their entry (Massive X crashed in a
//! module exit run on another thread). The fixture aborts the process if the threads differ.
mod common;
fn main() {
std::thread::spawn(|| {
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;
drop(soundcraft_vst3_host::instantiate_plugin(&gain).expect("instantiate"));
})
.join()
.expect("worker thread");
println!("module_exit_worker_thread: ok");
}