diff --git a/install/daw_vst_bridge.exe b/install/daw_vst_bridge.exe index 5847f84..d0b4ed2 100644 Binary files a/install/daw_vst_bridge.exe and b/install/daw_vst_bridge.exe differ diff --git a/native_bridge/src/main.cpp b/native_bridge/src/main.cpp index 39450f5..7fc729d 100644 --- a/native_bridge/src/main.cpp +++ b/native_bridge/src/main.cpp @@ -75,7 +75,7 @@ static void disable_ime_contexts(HWND w) { // G1.3: bo pump gate cu (g_juceOwnerTids / g_ownerTid / // is_teardown_window): teardown (close/LOAD job) va message pump cung chay tren - // 1 thread (UiThread) — job chay thi pump dung, job xong thi editor window da + // 1 thread (UiThread) � job chay thi pump dung, job xong thi editor window da // destroy nen USER32 tu huy message toi window chet. Khong con "2 thread trong // 1 DLL" khi teardown (multi-worker cu). @@ -295,7 +295,7 @@ public: for (int pumped = 0; pumped < 128; ++pumped) { if (!PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) break; hadMessages = true; - // G1.3: bo pump gate cu — teardown (close/LOAD job) + // G1.3: bo pump gate cu � teardown (close/LOAD job) // va pump cung 1 thread (UiThread): job chay thi pump dung; // het job thi editor window da destroy -> message chet bi // USER32 huy tu dong. Khong can drop theo close_in_flight. @@ -423,7 +423,7 @@ static bool close_in_flight(uint32_t ch) { - // G1.3: 1 UiThread — editor windows tao trong attach job (UiThread), destroy + // G1.3: 1 UiThread � editor windows tao trong attach job (UiThread), destroy // chi goi tu UiThread job (close_editor_now / LOAD) -> ownerTid luon == current // tid -> destroy truc tiep. Neu owner khac thread (khong con xay ra) -> bo // (leak > crash): cross-thread DestroyWindow khong an toan. @@ -1352,6 +1352,42 @@ int main(int argc, char* argv[]) { } + // V8 bug 5 (stale-reload watchdog): a close/attach job can hang + // inside createInstance (Nexus, observed) — reloading_ stays true + // forever, the channel and its same-plugin siblings stay muted, + // heartbeat keeps ticking so the Rust watchdog never respawns us: + // permanent silence with no log. Scan once per second; a reload + // older than 20s with no close/attach job in flight, or older than + // 60s regardless, is stale — force-clear so the audio loop processes + // the instance again. Risk (2 threads in one DLL) is bounded: SEH + // SafeProcessChannel re-mutes on crash; a hard DLL deadlock stalls + // writeIndex and Rust respawns the bridge (which recovers sound — + // observed: restart fixed muted channels). + { + static bool reloadWasSet[16] = { false }; + static std::chrono::steady_clock::time_point reloadSeen[16]; + static auto lastScan = std::chrono::steady_clock::now(); + auto nowScan = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(nowScan - lastScan).count() >= 1000) { + lastScan = nowScan; + for (uint32_t ch = 0; ch < 16; ++ch) { + bool rl = instruments.isReloading(ch); + if (!rl) { reloadWasSet[ch] = false; continue; } + if (!reloadWasSet[ch]) { reloadWasSet[ch] = true; reloadSeen[ch] = nowScan; continue; } + double sec = std::chrono::duration(nowScan - reloadSeen[ch]).count(); + bool jobInFlight = shmIPC->attachInFlight != 0 || close_in_flight(ch); + if (sec > 60.0 || (sec > 20.0 && !jobInFlight)) { + std::cerr << "[NativeBridge] STALE-RELOAD watchdog ch=" << ch + << " reloading " << (int)sec << "s (attachInFlight=" + << shmIPC->attachInFlight << " closeInFlight=" + << close_in_flight(ch) << ") — force unmute" << std::endl; + instruments.setReloading(ch, false); + reloadWasSet[ch] = false; + } + } + } + } + // B. Snapshot queued MIDI events (bounded copy, queue reset immediately) uint32_t nEvents = shmIPC->midiQueueCount > 64 ? 64 : shmIPC->midiQueueCount; SharedAudioBufferIPC::MidiEventIPC evts[64];