fix(bridge): noteId match noteOn (0) for noteOff; create VST GUI window on main thread, attach on worker

This commit is contained in:
2026-08-13 16:33:46 +07:00
parent 05e4244cc8
commit 2c890aef54
5 changed files with 226 additions and 103 deletions
+94 -1
View File
@@ -80,7 +80,7 @@ public:
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
"variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker\n");
"variants: main_own | worker_own_nopump | worker_own_pump | worker_foreign_nopump | worker_foreign_pump | bridge_like | same_thread | two_instances | two_instances_close | close_reopen | bridge_two | shared_worker | two_workers_close\n");
return 2;
}
std::string path = argv[1];
@@ -315,6 +315,99 @@ int main(int argc, char* argv[]) {
aDone.load() ? 1 : 0, aOk.load() ? 1 : 0,
bDone.load() ? 1 : 0, bOk.load() ? 1 : 0);
return (aDone.load() && aOk.load() && bDone.load() && bOk.load()) ? 0 : 3;
} else if (variant == "two_workers_close") {
// Round-4 exact bridge pattern: per-channel workers (own STA
// apartments); attach via temp thread + WORKER nested pump (the bridge
// main.cpp job pumps the worker queue while attachView runs on a
// temporary thread); close editor inst1 before attaching inst2 on its
// own worker.
ProbeWorker wa, wb;
wa.start();
wb.start();
std::atomic<bool> aDone{false}, aOk{false}, bDone{false}, bOk{false};
wa.post([&]() {
printf("[two_workers_close] workerA load inst1\n");
fflush(stdout);
bool r = inst.loadPlugin(path, 44100.0);
printf("[two_workers_close] workerA load inst1=%d\n", r ? 1 : 0);
fflush(stdout);
if (r) {
HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeA", WS_OVERLAPPEDWINDOW,
0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
printf("[two_workers_close] workerA hwnd=%p\n", (void*)h);
fflush(stdout);
bool r2 = false;
std::atomic<bool> d{false};
std::thread t([&]() {
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
r2 = inst.attachView(h);
CoUninitialize();
d = true;
});
while (!d.load()) {
MSG msg;
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
Sleep(1);
}
t.join();
printf("[two_workers_close] workerA attach inst1=%d\n", r2 ? 1 : 0);
fflush(stdout);
aOk = r2;
inst.closeGUI(); // view->removed() on worker A
printf("[two_workers_close] workerA closeGUI inst1\n");
fflush(stdout);
}
aDone = true;
});
auto t0 = std::chrono::steady_clock::now();
auto elapsed = [&]() {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::steady_clock::now() - t0).count();
};
while (!aDone.load() && elapsed() < secs) Sleep(50);
if (!aDone.load()) { printf("RESULT: TIMEOUT inst1 (aDone=0)\n"); return 3; }
wb.post([&]() {
printf("[two_workers_close] workerB load inst2\n");
fflush(stdout);
bool r = inst2.loadPlugin(path, 44100.0);
printf("[two_workers_close] workerB load inst2=%d\n", r ? 1 : 0);
fflush(stdout);
if (r) {
HWND h = CreateWindowEx(0, "GuiProbeClass", "ProbeB", WS_OVERLAPPEDWINDOW,
0, 0, 800, 600, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
printf("[two_workers_close] workerB hwnd=%p\n", (void*)h);
fflush(stdout);
bool r2 = false;
std::atomic<bool> d{false};
std::thread t([&]() {
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
r2 = inst2.attachView(h);
CoUninitialize();
d = true;
});
while (!d.load()) {
MSG msg;
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
Sleep(1);
}
t.join();
printf("[two_workers_close] workerB attach inst2=%d\n", r2 ? 1 : 0);
fflush(stdout);
bOk = r2;
}
bDone = true;
});
while ((!aDone.load() || !bDone.load()) && elapsed() < secs) Sleep(50);
printf("RESULT: aDone=%d aOk=%d bDone=%d bOk=%d\n",
aDone.load() ? 1 : 0, aOk.load() ? 1 : 0,
bDone.load() ? 1 : 0, bOk.load() ? 1 : 0);
return (aDone.load() && aOk.load() && bDone.load() && bOk.load()) ? 0 : 3;
} else if (variant == "two_instances" || variant == "two_instances_close" || variant == "close_reopen") {
// Bridge round-3 probes: does a SECOND instance of the same plugin hang
// in view->attached() while the first instance's view is attached?