fix(bridge): closeGUI on channel worker thread — WM_DESTROY runs on main, view->removed() must run on the worker apartment or plugin corrupts and next attach hangs
This commit is contained in:
@@ -55,25 +55,23 @@ static void sleep_ms(uint32_t ms) {
|
|||||||
// thread cua channel tao window) co the don map. USERDATA luu channel+1 (KHONG
|
// thread cua channel tao window) co the don map. USERDATA luu channel+1 (KHONG
|
||||||
// luu con tro inst truc tiep: assign() thay inst moi moi lan load — con tro cu
|
// luu con tro inst truc tiep: assign() thay inst moi moi lan load — con tro cu
|
||||||
// bi huy → WM_DESTROY tren con tro dangling → crash/hang bridge).
|
// bi huy → WM_DESTROY tren con tro dangling → crash/hang bridge).
|
||||||
|
class ChannelWorker; // fwd — WM_DESTROY posts closeGUI() to the channel worker
|
||||||
|
static void post_close_gui(uint32_t ch, HWND hwnd); // defined after ChannelWorker
|
||||||
static InstrumentEngineManager* g_engine = nullptr;
|
static InstrumentEngineManager* g_engine = nullptr;
|
||||||
static std::mutex g_guiMutex;
|
static std::mutex g_guiMutex;
|
||||||
static std::map<uint32_t, void*> g_guiWindows; // channel -> HWND (keep window alive)
|
static std::map<uint32_t, void*> g_guiWindows; // channel -> HWND (keep window alive)
|
||||||
static std::map<HWND, uint32_t> g_hwndToCh; // HWND -> channel (WM_DESTROY cleanup)
|
static std::map<HWND, uint32_t> g_hwndToCh; // HWND -> channel (WM_DESTROY cleanup)
|
||||||
|
static std::map<uint32_t, std::unique_ptr<ChannelWorker>>* g_workers = nullptr;
|
||||||
|
|
||||||
static LRESULT CALLBACK VstWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
static LRESULT CALLBACK VstWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||||
if (uMsg == WM_DESTROY) {
|
if (uMsg == WM_DESTROY) {
|
||||||
INativeInstrument* instToClose = nullptr;
|
uint32_t ch = UINT32_MAX;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(g_guiMutex);
|
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||||
auto it = g_hwndToCh.find(hwnd);
|
auto it = g_hwndToCh.find(hwnd);
|
||||||
if (it != g_hwndToCh.end()) {
|
if (it != g_hwndToCh.end()) ch = it->second;
|
||||||
uint32_t ch = it->second;
|
|
||||||
g_hwndToCh.erase(it);
|
|
||||||
g_guiWindows.erase(ch);
|
|
||||||
if (g_engine) instToClose = g_engine->get(ch);
|
|
||||||
}
|
}
|
||||||
}
|
if (ch != UINT32_MAX) post_close_gui(ch, hwnd);
|
||||||
if (instToClose) instToClose->closeGUI();
|
|
||||||
}
|
}
|
||||||
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
@@ -171,6 +169,32 @@ private:
|
|||||||
bool stop_ = false;
|
bool stop_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// closeGUI() MUST run on the channel worker thread (its COM STA apartment) —
|
||||||
|
// the view was attached there. Calling view->removed() from the main thread
|
||||||
|
// (WM_DESTROY handler) is a cross-apartment COM call that corrupts the plugin;
|
||||||
|
// Nexus then hangs on the NEXT view->attached(). Erase the registry inside the
|
||||||
|
// job so Option B (closing another editor before attach) waits for closeGUI to
|
||||||
|
// actually finish.
|
||||||
|
static void post_close_gui(uint32_t ch, HWND hwnd) {
|
||||||
|
ChannelWorker* w = nullptr;
|
||||||
|
if (g_workers) {
|
||||||
|
auto wit = g_workers->find(ch);
|
||||||
|
if (wit != g_workers->end()) w = wit->second.get();
|
||||||
|
}
|
||||||
|
if (w) {
|
||||||
|
w->post([ch, hwnd]() {
|
||||||
|
if (auto* i = g_engine->get(ch)) i->closeGUI();
|
||||||
|
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||||
|
g_hwndToCh.erase(hwnd);
|
||||||
|
g_guiWindows.erase(ch);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
std::lock_guard<std::mutex> lock(g_guiMutex);
|
||||||
|
g_hwndToCh.erase(hwnd);
|
||||||
|
g_guiWindows.erase(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
std::cout << "[NativeBridge] Starting DAW Host Bridge Engine..." << std::endl;
|
std::cout << "[NativeBridge] Starting DAW Host Bridge Engine..." << std::endl;
|
||||||
|
|
||||||
@@ -209,6 +233,7 @@ int main(int argc, char* argv[]) {
|
|||||||
// thread whose COM STA apartment stays alive for the channel's lifetime
|
// thread whose COM STA apartment stays alive for the channel's lifetime
|
||||||
// (see ChannelWorker comment — a dead apartment hangs Nexus attached()).
|
// (see ChannelWorker comment — a dead apartment hangs Nexus attached()).
|
||||||
std::map<uint32_t, std::unique_ptr<ChannelWorker>> workers;
|
std::map<uint32_t, std::unique_ptr<ChannelWorker>> workers;
|
||||||
|
g_workers = &workers;
|
||||||
// B9: native editor windows per channel — keep alive (HWND outlives the job).
|
// B9: native editor windows per channel — keep alive (HWND outlives the job).
|
||||||
// Registry la global (g_guiWindows) — WM_DESTROY cleanup can tu VstWindowProc.
|
// Registry la global (g_guiWindows) — WM_DESTROY cleanup can tu VstWindowProc.
|
||||||
// B8: sample rate from the DAW (Rust spawns us with SF_SAMPLE_RATE).
|
// B8: sample rate from the DAW (Rust spawns us with SF_SAMPLE_RATE).
|
||||||
|
|||||||
Reference in New Issue
Block a user