From 3587a34e3122fd3fa092fc287694cab5f7c454c6 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Sat, 15 Aug 2026 20:50:43 +0700 Subject: [PATCH] G1.1: fix probe crash (std::thread joinable dtor -> terminate); xac nhan openGUI tren worker thread OK; closeGUI truoc teardown --- native_bridge/CMakeLists.txt | 3 +- native_bridge/src/gui_probe.cpp | 68 +++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/native_bridge/CMakeLists.txt b/native_bridge/CMakeLists.txt index 27d3e63..5ece1dc 100644 --- a/native_bridge/CMakeLists.txt +++ b/native_bridge/CMakeLists.txt @@ -89,10 +89,11 @@ add_executable(gui_probe ) if(VST3_SDK_TARGET) target_compile_definitions(gui_probe PRIVATE HAVE_VST3SDK=1) - target_link_libraries(gui_probe PRIVATE ${VST3_SDK_TARGET} sdk_hosting) + target_link_libraries(gui_probe PRIVATE ${VST3_SDK_TARGET} sdk_hosting sdk_common) if(WIN32) target_sources(gui_probe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vst3sdk/public.sdk/source/vst/hosting/module_win32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/vst3sdk/public.sdk/source/common/memorystream.cpp ) endif() endif() diff --git a/native_bridge/src/gui_probe.cpp b/native_bridge/src/gui_probe.cpp index 30ca659..42d0753 100644 --- a/native_bridge/src/gui_probe.cpp +++ b/native_bridge/src/gui_probe.cpp @@ -17,13 +17,27 @@ #include #include #include +#include + +#ifdef _WIN32 +static void probe_invalid_param(const wchar_t* expr, const wchar_t* func, const wchar_t* file, + unsigned int line, uintptr_t reserved) { + printf("[probe] INVALID_PARAMETER expr=%ls func=%ls\n", expr ? expr : L"null", func ? func : L"null"); + fflush(stdout); +} +#endif static void pump_for(int secs) { +#ifdef _WIN32 + _set_invalid_parameter_handler(probe_invalid_param); +#endif auto t0 = std::chrono::steady_clock::now(); while (std::chrono::duration_cast( std::chrono::steady_clock::now() - t0).count() < secs) { MSG msg; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + printf("[pump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd); + fflush(stdout); TranslateMessage(&msg); DispatchMessageW(&msg); } @@ -39,7 +53,8 @@ public: void start() { th = std::thread([this] { #ifdef _WIN32 - OleInitialize(nullptr); + _set_invalid_parameter_handler(probe_invalid_param); + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); #endif std::unique_lock lk(mu); for (;;) { @@ -48,21 +63,29 @@ public: auto j = std::move(jobs.front()); jobs.pop_front(); lk.unlock(); - j(); + try { j(); } + catch (const std::exception& e) { printf("[worker] JOB EXCEPTION: %s\n", e.what()); fflush(stdout); } + catch (...) { printf("[worker] JOB UNKNOWN EXCEPTION\n"); fflush(stdout); } lk.lock(); continue; } cv.wait_for(lk, std::chrono::milliseconds(5)); lk.unlock(); - MSG msg; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessageW(&msg); + try { + MSG msg; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + printf("[wpump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd); + fflush(stdout); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } } + catch (const std::exception& e) { printf("[worker] PUMP EXCEPTION: %s\n", e.what()); fflush(stdout); } + catch (...) { printf("[worker] PUMP UNKNOWN EXCEPTION\n"); fflush(stdout); } lk.lock(); } #ifdef _WIN32 - OleUninitialize(); + CoUninitialize(); #endif }); } @@ -70,6 +93,11 @@ public: { std::lock_guard lk(mu); jobs.push_back(std::move(j)); } cv.notify_all(); } + ~ProbeWorker() { + { std::lock_guard lk(mu); stop = true; } + cv.notify_all(); + if (th.joinable()) th.join(); + } std::thread th; std::mutex mu; std::condition_variable cv; @@ -80,7 +108,7 @@ public: int main(int argc, char* argv[]) { if (argc < 2) { printf("usage: gui_probe [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 | two_workers_close\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 | ui_thread | two_workers_close\n"); return 2; } std::string path = argv[1]; @@ -120,7 +148,7 @@ int main(int argc, char* argv[]) { bool isDeferred = (variant == "bridge_like" || variant == "same_thread" || variant == "two_instances" || variant == "two_instances_close" || variant == "close_reopen" || variant == "bridge_two" || - variant == "shared_worker"); + variant == "shared_worker" || variant == "ui_thread"); bool ok = true; if (!isDeferred) { ok = inst.loadPlugin(path, 44100.0); @@ -149,10 +177,12 @@ int main(int argc, char* argv[]) { } else if (variant == "worker_own_nopump" || variant == "worker_foreign_nopump") { std::thread t(attach_job); sleep_for(secs); + if (t.joinable()) t.join(); if (!done.load()) { printf("RESULT: TIMEOUT (no pump)\n"); return 3; } } else if (variant == "worker_own_pump" || variant == "worker_foreign_pump") { std::thread t(attach_job); pump_for(secs); + if (t.joinable()) t.join(); if (!done.load()) { printf("RESULT: TIMEOUT (pump concurrent)\n"); return 3; } } else if (variant == "bridge_like") { // Bridge pattern: plugin loaded on thread A which then EXITS (COM @@ -169,6 +199,7 @@ int main(int argc, char* argv[]) { loadA.join(); std::thread t(attach_job); // thread B, after A exited pump_for(secs); + if (t.joinable()) t.join(); if (!done.load()) { printf("RESULT: TIMEOUT (apartment dead)\n"); return 3; } } else if (variant == "same_thread") { // Load and openGUI on the SAME worker thread, kept alive; main pumps. @@ -179,11 +210,12 @@ int main(int argc, char* argv[]) { bool r = inst.loadPlugin(path, 44100.0); printf("[same_thread] load=%d\n", r ? 1 : 0); fflush(stdout); - if (r) attach_job(); + if (r) { attach_job(); } }); pump_for(secs); + if (t.joinable()) t.join(); if (!done.load()) { printf("RESULT: TIMEOUT (same_thread)\n"); return 3; } - } else if (variant == "shared_worker") { + } else if (variant == "shared_worker" || variant == "ui_thread") { // Proposed round-3 fix: ALL VST3 instances live in ONE apartment — // one persistent worker thread hosts load + openGUI for every channel. ProbeWorker w; @@ -215,7 +247,9 @@ int main(int argc, char* argv[]) { } done = true; }); + w.post([&]() { inst.closeGUI(); inst2.closeGUI(); }); auto t0 = std::chrono::steady_clock::now(); + try { while (!done.load() && std::chrono::duration_cast( std::chrono::steady_clock::now() - t0).count() < secs) { MSG msg; @@ -225,10 +259,13 @@ int main(int argc, char* argv[]) { } Sleep(2); } + } catch (const std::exception& e) { printf("[main] PUMP1 EXCEPTION: %s\n", e.what()); fflush(stdout); } + catch (...) { printf("[main] PUMP1 UNKNOWN EXCEPTION\n"); fflush(stdout); } // Stay alive 5s with BOTH editors attached and the worker pumping, // while MAIN KEEPS PUMPING like the bridge audio loop: distinguishes a // live crash (bad) from a stop-pumping probe artifact. auto t1 = std::chrono::steady_clock::now(); + try { while (std::chrono::duration_cast( std::chrono::steady_clock::now() - t1).count() < 5) { MSG msg; @@ -238,6 +275,8 @@ int main(int argc, char* argv[]) { } Sleep(2); } + } catch (const std::exception& e) { printf("[main] PUMP2 EXCEPTION: %s\n", e.what()); fflush(stdout); } + catch (...) { printf("[main] PUMP2 UNKNOWN EXCEPTION\n"); fflush(stdout); } printf("RESULT: done=%d aRes=%d bRes=%d\n", done.load() ? 1 : 0, aRes, bRes); return (done.load() && aRes && bRes) ? 0 : 3; } else if (variant == "bridge_two") { @@ -347,6 +386,8 @@ int main(int argc, char* argv[]) { while (!d.load()) { MSG msg; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + printf("[wpump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd); + fflush(stdout); TranslateMessage(&msg); DispatchMessageW(&msg); } @@ -391,6 +432,8 @@ int main(int argc, char* argv[]) { while (!d.load()) { MSG msg; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + printf("[wpump] msg=0x%X hwnd=%p\n", (unsigned)msg.message, (void*)msg.hwnd); + fflush(stdout); TranslateMessage(&msg); DispatchMessageW(&msg); } @@ -478,6 +521,7 @@ int main(int argc, char* argv[]) { } // 5s survival with both editors attached (main keeps pumping). auto t1 = std::chrono::steady_clock::now(); + try { while (std::chrono::duration_cast( std::chrono::steady_clock::now() - t1).count() < 5) { MSG msg; @@ -487,6 +531,8 @@ int main(int argc, char* argv[]) { } Sleep(2); } + } catch (const std::exception& e) { printf("[main] PUMP2 EXCEPTION: %s\n", e.what()); fflush(stdout); } + catch (...) { printf("[main] PUMP2 UNKNOWN EXCEPTION\n"); fflush(stdout); } printf("RESULT: done=%d attached_ok=%d\n", 1, 1); return 0; } else {