diff --git a/install/daw_vst_bridge.exe b/install/daw_vst_bridge.exe index 83389a8..58d88df 100644 Binary files a/install/daw_vst_bridge.exe and b/install/daw_vst_bridge.exe differ diff --git a/install/sonicforge-daw.exe b/install/sonicforge-daw.exe index 9c4d1a1..a25266d 100644 Binary files a/install/sonicforge-daw.exe and b/install/sonicforge-daw.exe differ diff --git a/native_bridge/include/SharedMemoryIPC.h b/native_bridge/include/SharedMemoryIPC.h index 25c4c33..c32224f 100644 --- a/native_bridge/include/SharedMemoryIPC.h +++ b/native_bridge/include/SharedMemoryIPC.h @@ -49,4 +49,10 @@ struct SharedAudioBufferIPC { char arg2[1024]; // LOAD: UTF-8 path } controlQueue[8]; volatile uint32_t controlQueueCount; + + // G0.3: set by the bridge while an openGUI attach job is in flight + // (reload/attachView on a ChannelWorker). The Tauri watchdog reads this + // to extend its stall deadline — a long Nexus attach must not look like + // a dead bridge. Append-only field: existing offsets stay stable. + volatile uint32_t attachInFlight; }; diff --git a/native_bridge/src/main.cpp b/native_bridge/src/main.cpp index 08b8701..3e30532 100644 --- a/native_bridge/src/main.cpp +++ b/native_bridge/src/main.cpp @@ -940,6 +940,14 @@ int main(int argc, char* argv[]) { // a window on a worker whose pump is idle during jobs hangs // view->attached() (gui_probe: two_workers_close TIMEOUT). The attach // job runs on the channel worker (COM STA apartment stays alive). + // G0.3: while an attach job is in flight, tell the Tauri watchdog via + // SHM so a long Nexus reload/attach (can exceed 3s) is not mistaken + // for a dead bridge and killed. RAII -> cleared on every exit path. + struct AttachInFlightGuard { + SharedAudioBufferIPC* ipc; + explicit AttachInFlightGuard(SharedAudioBufferIPC* p) : ipc(p) { if (ipc) ipc->attachInFlight = 1; } + ~AttachInFlightGuard() { if (ipc) ipc->attachInFlight = 0; } + }; auto handleOpenGui = [&](uint32_t guiCh, uintptr_t arg1, const std::string& pluginId) { if (!instruments.get(guiCh)) { std::cerr << "[NativeBridge] GUI deferred ch=" << guiCh @@ -1029,7 +1037,8 @@ int main(int argc, char* argv[]) { instruments.setReloading(guiCh, true); } } - workers[guiCh]->post([&instruments, guiCh, hwnd, arg2 = pluginId]() { + workers[guiCh]->post([&instruments, shmIPC, guiCh, hwnd, arg2 = pluginId]() { + AttachInFlightGuard _aifGuard{shmIPC}; if (!instruments.get(guiCh)) { std::cerr << "[NativeBridge] GUI attach FAILED hwnd=" << hwnd << " plugin=" << arg2 << " ch=" << guiCh << " (no instrument loaded)" << std::endl; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index cd02f88..f2f7829 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -383,6 +383,21 @@ pub fn run() { false } }); + // G0.3: an openGUI attach job (reload/attachView on the + // bridge worker) can legitimately outlast the 3s stall + // deadline if it blocks the audio loop's message pump — + // do not kill the bridge for that. Real stalls are still + // caught: attach never takes > 15s; a dead bridge leaves + // the flag stuck and is restarted after the longer wait. + let attach_deadline = guard + .as_ref() + .map(|s| s.attach_in_flight()) + .unwrap_or(false); + let stall_deadline = if attach_deadline { + std::time::Duration::from_secs(15) + } else { + std::time::Duration::from_secs(3) + }; if advanced == Some(true) { last_change = std::time::Instant::now(); down_emitted = false; @@ -394,7 +409,7 @@ pub fn run() { "bridge-audio", AudioFrame { l: l.to_vec(), r: r.to_vec() }, ); - } else if last_change.elapsed() >= std::time::Duration::from_secs(3) && !down_emitted { + } else if last_change.elapsed() >= stall_deadline && !down_emitted { // Check lai truoc khi respawn — Destroyed co the vua set flag. if SHUTTING_DOWN.load(Ordering::Relaxed) { break; diff --git a/src-tauri/src/shm.rs b/src-tauri/src/shm.rs index 64da131..01cecdb 100644 --- a/src-tauri/src/shm.rs +++ b/src-tauri/src/shm.rs @@ -59,6 +59,10 @@ pub struct SharedAudioBufferIPC { pub midi_queue_count: u32, pub control_queue: [ControlEventIPC; CONTROL_QUEUE_CAP], pub control_queue_count: u32, + // G0.3: bridge sets this while an openGUI attach job is in flight. + // Append-only field — offsets of control_queue (2836) and + // control_queue_count (11156) are unchanged. + pub attach_in_flight: u32, } pub struct Shm { @@ -158,6 +162,10 @@ impl Shm { pub fn block_timestamp(&self) -> u64 { self.ipc().block_timestamp } + + pub fn attach_in_flight(&self) -> bool { + self.ipc().attach_in_flight != 0 + } } impl Drop for Shm { @@ -180,6 +188,6 @@ mod tests { // native_bridge/tests/shm_selfcheck.cpp asserts the same sizes. assert_eq!(std::mem::size_of::(), 12); assert_eq!(std::mem::size_of::(), 1040); - assert_eq!(std::mem::size_of::(), 11160); + assert_eq!(std::mem::size_of::(), 11164); } }