185| attach_job(); // editorhost pattern: attached on main thread, pump after 186| pump_for(secs); 187| } else if (variant == "worker_own_nopump" || variant == "worker_foreign_nopump") { 188| std::thread t(attach_job); 189| sleep_for(secs); 190| if (!done.load()) { printf("RESULT: TIMEOUT (no pump)\n"); return 3; } 191| } else if (variant == "worker_own_pump" || variant == "worker_foreign_pump") { 192| std::thread t(attach_job); 193| pump_for(secs); 194| if (!done.load()) { printf("RESULT: TIMEOUT (pump concurrent)\n"); return 3; } 195| } else if (variant == "bridge_like") { 196| // Bridge pattern: plugin loaded on thread A which then EXITS (COM 197| // apartment dies), openGUI on separate thread B, main pumps. 198| std::thread loadA([&]() { 199| CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 200| printf("[bridge_like] loadPlugin on thread A\n"); 201| fflush(stdout); 202| bool r = inst.loadPlugin(path, 44100.0); 203| printf("[bridge_like] load=%d (thread A exiting)\n", r ? 1 : 0); 204| fflush(stdout); 205| CoUninitialize(); 206| }); 207| loadA.join(); 208| std::thread t(attach_job); // thread B, after A exited 209| pump_for(secs); 210| if (!done.load()) { printf("RESULT: TIMEOUT (apartment dead)\n"); return 3; } 211| } else if (variant == "same_thread") { 212| // Load and openGUI on the SAME worker thread, kept alive; main pumps. 213| std::thread t([&]() { 214| CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 215| ImmAssociateContextEx(nullptr, nullptr, IACE_DEFAULT); 216| printf("[same_thread] loadPlugin on worker\n"); 217| fflush(stdout); 218| bool r = inst.loadPlugin(path, 44100.0); 219| printf("[same_thread] load=%d\n", r ? 1 : 0); 220| fflush(stdout); 221| if (r) { disable_ime_contexts((HWND)targetHwnd); attach_job(); disable_ime_contexts((HWND)targetHwnd); } 222| }); 223| pump_for(secs); 224| if (!done.load()) { printf("RESULT: TIMEOUT (same_thread)\n"); return 3; } 225| } else if (variant == "shared_worker" || variant == "ui_thread") { 226| // Proposed round-3 fix: ALL VST3 instances live in ONE apartment — 227| // one persistent worker thread hosts load + openGUI for every channel. 228| ProbeWorker w; 229| w.start(); 230| std::atomic done{false}; 231| int aRes = 0, bRes = 0; 232| w.post([&]() {