fix: stale-reload watchdog — force-unmute channel stuck in reloading >20s (60s if close/attach in flight) (V9)

This commit is contained in:
2026-08-16 16:14:25 +07:00
parent 4c8677699c
commit c29e024372
2 changed files with 39 additions and 3 deletions
+39 -3
View File
@@ -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<std::chrono::milliseconds>(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<double>(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];