G1.1: fix probe crash (std::thread joinable dtor -> terminate); xac nhan openGUI tren worker thread OK; closeGUI truoc teardown
This commit is contained in:
@@ -89,10 +89,11 @@ add_executable(gui_probe
|
|||||||
)
|
)
|
||||||
if(VST3_SDK_TARGET)
|
if(VST3_SDK_TARGET)
|
||||||
target_compile_definitions(gui_probe PRIVATE HAVE_VST3SDK=1)
|
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)
|
if(WIN32)
|
||||||
target_sources(gui_probe PRIVATE
|
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/vst/hosting/module_win32.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vst3sdk/public.sdk/source/common/memorystream.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -17,13 +17,27 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#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) {
|
static void pump_for(int secs) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
_set_invalid_parameter_handler(probe_invalid_param);
|
||||||
|
#endif
|
||||||
auto t0 = std::chrono::steady_clock::now();
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
while (std::chrono::duration_cast<std::chrono::seconds>(
|
while (std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::steady_clock::now() - t0).count() < secs) {
|
std::chrono::steady_clock::now() - t0).count() < secs) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
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);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&msg);
|
DispatchMessageW(&msg);
|
||||||
}
|
}
|
||||||
@@ -39,7 +53,8 @@ public:
|
|||||||
void start() {
|
void start() {
|
||||||
th = std::thread([this] {
|
th = std::thread([this] {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
OleInitialize(nullptr);
|
_set_invalid_parameter_handler(probe_invalid_param);
|
||||||
|
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
#endif
|
#endif
|
||||||
std::unique_lock<std::mutex> lk(mu);
|
std::unique_lock<std::mutex> lk(mu);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -48,21 +63,29 @@ public:
|
|||||||
auto j = std::move(jobs.front());
|
auto j = std::move(jobs.front());
|
||||||
jobs.pop_front();
|
jobs.pop_front();
|
||||||
lk.unlock();
|
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();
|
lk.lock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cv.wait_for(lk, std::chrono::milliseconds(5));
|
cv.wait_for(lk, std::chrono::milliseconds(5));
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
|
try {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
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);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&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();
|
lk.lock();
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
OleUninitialize();
|
CoUninitialize();
|
||||||
#endif
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,6 +93,11 @@ public:
|
|||||||
{ std::lock_guard<std::mutex> lk(mu); jobs.push_back(std::move(j)); }
|
{ std::lock_guard<std::mutex> lk(mu); jobs.push_back(std::move(j)); }
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
}
|
}
|
||||||
|
~ProbeWorker() {
|
||||||
|
{ std::lock_guard<std::mutex> lk(mu); stop = true; }
|
||||||
|
cv.notify_all();
|
||||||
|
if (th.joinable()) th.join();
|
||||||
|
}
|
||||||
std::thread th;
|
std::thread th;
|
||||||
std::mutex mu;
|
std::mutex mu;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
@@ -80,7 +108,7 @@ public:
|
|||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
printf("usage: gui_probe <plugin.vst3> [variant] [secs] [hwnd]\n"
|
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 | 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;
|
return 2;
|
||||||
}
|
}
|
||||||
std::string path = argv[1];
|
std::string path = argv[1];
|
||||||
@@ -120,7 +148,7 @@ int main(int argc, char* argv[]) {
|
|||||||
bool isDeferred = (variant == "bridge_like" || variant == "same_thread" ||
|
bool isDeferred = (variant == "bridge_like" || variant == "same_thread" ||
|
||||||
variant == "two_instances" || variant == "two_instances_close" ||
|
variant == "two_instances" || variant == "two_instances_close" ||
|
||||||
variant == "close_reopen" || variant == "bridge_two" ||
|
variant == "close_reopen" || variant == "bridge_two" ||
|
||||||
variant == "shared_worker");
|
variant == "shared_worker" || variant == "ui_thread");
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (!isDeferred) {
|
if (!isDeferred) {
|
||||||
ok = inst.loadPlugin(path, 44100.0);
|
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") {
|
} else if (variant == "worker_own_nopump" || variant == "worker_foreign_nopump") {
|
||||||
std::thread t(attach_job);
|
std::thread t(attach_job);
|
||||||
sleep_for(secs);
|
sleep_for(secs);
|
||||||
|
if (t.joinable()) t.join();
|
||||||
if (!done.load()) { printf("RESULT: TIMEOUT (no pump)\n"); return 3; }
|
if (!done.load()) { printf("RESULT: TIMEOUT (no pump)\n"); return 3; }
|
||||||
} else if (variant == "worker_own_pump" || variant == "worker_foreign_pump") {
|
} else if (variant == "worker_own_pump" || variant == "worker_foreign_pump") {
|
||||||
std::thread t(attach_job);
|
std::thread t(attach_job);
|
||||||
pump_for(secs);
|
pump_for(secs);
|
||||||
|
if (t.joinable()) t.join();
|
||||||
if (!done.load()) { printf("RESULT: TIMEOUT (pump concurrent)\n"); return 3; }
|
if (!done.load()) { printf("RESULT: TIMEOUT (pump concurrent)\n"); return 3; }
|
||||||
} else if (variant == "bridge_like") {
|
} else if (variant == "bridge_like") {
|
||||||
// Bridge pattern: plugin loaded on thread A which then EXITS (COM
|
// Bridge pattern: plugin loaded on thread A which then EXITS (COM
|
||||||
@@ -169,6 +199,7 @@ int main(int argc, char* argv[]) {
|
|||||||
loadA.join();
|
loadA.join();
|
||||||
std::thread t(attach_job); // thread B, after A exited
|
std::thread t(attach_job); // thread B, after A exited
|
||||||
pump_for(secs);
|
pump_for(secs);
|
||||||
|
if (t.joinable()) t.join();
|
||||||
if (!done.load()) { printf("RESULT: TIMEOUT (apartment dead)\n"); return 3; }
|
if (!done.load()) { printf("RESULT: TIMEOUT (apartment dead)\n"); return 3; }
|
||||||
} else if (variant == "same_thread") {
|
} else if (variant == "same_thread") {
|
||||||
// Load and openGUI on the SAME worker thread, kept alive; main pumps.
|
// 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);
|
bool r = inst.loadPlugin(path, 44100.0);
|
||||||
printf("[same_thread] load=%d\n", r ? 1 : 0);
|
printf("[same_thread] load=%d\n", r ? 1 : 0);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (r) attach_job();
|
if (r) { attach_job(); }
|
||||||
});
|
});
|
||||||
pump_for(secs);
|
pump_for(secs);
|
||||||
|
if (t.joinable()) t.join();
|
||||||
if (!done.load()) { printf("RESULT: TIMEOUT (same_thread)\n"); return 3; }
|
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 —
|
// Proposed round-3 fix: ALL VST3 instances live in ONE apartment —
|
||||||
// one persistent worker thread hosts load + openGUI for every channel.
|
// one persistent worker thread hosts load + openGUI for every channel.
|
||||||
ProbeWorker w;
|
ProbeWorker w;
|
||||||
@@ -215,7 +247,9 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
});
|
});
|
||||||
|
w.post([&]() { inst.closeGUI(); inst2.closeGUI(); });
|
||||||
auto t0 = std::chrono::steady_clock::now();
|
auto t0 = std::chrono::steady_clock::now();
|
||||||
|
try {
|
||||||
while (!done.load() && std::chrono::duration_cast<std::chrono::seconds>(
|
while (!done.load() && std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::steady_clock::now() - t0).count() < secs) {
|
std::chrono::steady_clock::now() - t0).count() < secs) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
@@ -225,10 +259,13 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
Sleep(2);
|
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,
|
// Stay alive 5s with BOTH editors attached and the worker pumping,
|
||||||
// while MAIN KEEPS PUMPING like the bridge audio loop: distinguishes a
|
// while MAIN KEEPS PUMPING like the bridge audio loop: distinguishes a
|
||||||
// live crash (bad) from a stop-pumping probe artifact.
|
// live crash (bad) from a stop-pumping probe artifact.
|
||||||
auto t1 = std::chrono::steady_clock::now();
|
auto t1 = std::chrono::steady_clock::now();
|
||||||
|
try {
|
||||||
while (std::chrono::duration_cast<std::chrono::seconds>(
|
while (std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::steady_clock::now() - t1).count() < 5) {
|
std::chrono::steady_clock::now() - t1).count() < 5) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
@@ -238,6 +275,8 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
Sleep(2);
|
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);
|
printf("RESULT: done=%d aRes=%d bRes=%d\n", done.load() ? 1 : 0, aRes, bRes);
|
||||||
return (done.load() && aRes && bRes) ? 0 : 3;
|
return (done.load() && aRes && bRes) ? 0 : 3;
|
||||||
} else if (variant == "bridge_two") {
|
} else if (variant == "bridge_two") {
|
||||||
@@ -347,6 +386,8 @@ int main(int argc, char* argv[]) {
|
|||||||
while (!d.load()) {
|
while (!d.load()) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
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);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&msg);
|
DispatchMessageW(&msg);
|
||||||
}
|
}
|
||||||
@@ -391,6 +432,8 @@ int main(int argc, char* argv[]) {
|
|||||||
while (!d.load()) {
|
while (!d.load()) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
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);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&msg);
|
DispatchMessageW(&msg);
|
||||||
}
|
}
|
||||||
@@ -478,6 +521,7 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
// 5s survival with both editors attached (main keeps pumping).
|
// 5s survival with both editors attached (main keeps pumping).
|
||||||
auto t1 = std::chrono::steady_clock::now();
|
auto t1 = std::chrono::steady_clock::now();
|
||||||
|
try {
|
||||||
while (std::chrono::duration_cast<std::chrono::seconds>(
|
while (std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
std::chrono::steady_clock::now() - t1).count() < 5) {
|
std::chrono::steady_clock::now() - t1).count() < 5) {
|
||||||
MSG msg;
|
MSG msg;
|
||||||
@@ -487,6 +531,8 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
Sleep(2);
|
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);
|
printf("RESULT: done=%d attached_ok=%d\n", 1, 1);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user