fix(api,ui): chan double-spawn GUI embed master chain - register bridge ngay sau Popen + inflight guard + frontend busy ref (crash 0xc0000005 2 instance Ozone)
This commit is contained in:
+36
-20
@@ -719,10 +719,17 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
if not plugin_path:
|
||||
raise HTTPException(status_code=400, detail="Thiếu đường dẫn VST plugin.")
|
||||
# Tránh mở 2 GUI cùng plugin: nếu tiến trình bridge fx-gui cho plugin này
|
||||
# đang chạy → trả started=False (frontend có thể focus window cũ).
|
||||
# đang chạy (đã register sau Popen, kể cả khi còn đang chờ attach verdict)
|
||||
# → trả started=False (frontend có thể focus window cũ).
|
||||
for proc in _FX_GUI_PROCESSES:
|
||||
if proc is not None and proc.poll() is None and getattr(proc, "_sf_plugin_path", None) == plugin_path:
|
||||
return {"success": True, "started": False, "already_running": True, "cmd": [], "embed_url": getattr(proc, "_sf_embed_url", None) or None}
|
||||
# Request thứ 2 cho cùng plugin trong lúc spawn đầu ĐANG CHỜ verdict (có thể
|
||||
# 20-70s cho Ozone-class) chưa thấy process nào register → phải chặn bằng
|
||||
# inflight set, nếu không sẽ spawn bridge thứ 2 → 2 GUI instance Ozone cùng
|
||||
# lúc → crash (0xc0000005 iZOzone11Core.dll) như bug đã gặp.
|
||||
if req.embed and plugin_path in _FX_GUI_INFLIGHT:
|
||||
return {"success": True, "started": False, "already_running": True, "cmd": [], "embed_url": None}
|
||||
job = {"path": plugin_path, "name": req.name or os.path.basename(plugin_path)}
|
||||
if req.shm:
|
||||
job["shm"] = req.shm
|
||||
@@ -743,23 +750,25 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
|
||||
# in-process retry cannot recover from a crashed core DLL state).
|
||||
# Runs in a worker thread so the API event loop is not blocked for
|
||||
# the (long) load+attach+retry window.
|
||||
proc, embed_url = await asyncio.to_thread(_spawn_fx_gui_embed, cmd)
|
||||
if not embed_url:
|
||||
_kill_fx_gui_proc(proc)
|
||||
try:
|
||||
os.remove(job_path)
|
||||
except Exception:
|
||||
pass
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Không mở được GUI embed cho plugin — bridge thất bại "
|
||||
"nhiều lần khi tải/mở editor (chi tiết trong log [fx-gui]).",
|
||||
)
|
||||
proc._sf_plugin_path = plugin_path
|
||||
proc._sf_embed_url = embed_url
|
||||
_register_fx_gui_process(proc)
|
||||
return {"success": True, "started": True, "already_running": False,
|
||||
"embed_url": embed_url, "cmd": cmd}
|
||||
_FX_GUI_INFLIGHT.add(plugin_path)
|
||||
try:
|
||||
proc, embed_url = await asyncio.to_thread(_spawn_fx_gui_embed, cmd, plugin_path)
|
||||
if not embed_url:
|
||||
_kill_fx_gui_proc(proc)
|
||||
try:
|
||||
os.remove(job_path)
|
||||
except Exception:
|
||||
pass
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Không mở được GUI embed cho plugin — bridge thất bại "
|
||||
"nhiều lần khi tải/mở editor (chi tiết trong log [fx-gui]).",
|
||||
)
|
||||
proc._sf_embed_url = embed_url
|
||||
return {"success": True, "started": True, "already_running": False,
|
||||
"embed_url": embed_url, "cmd": cmd}
|
||||
finally:
|
||||
_FX_GUI_INFLIGHT.discard(plugin_path)
|
||||
proc = subprocess.Popen(cmd, close_fds=os.name != "nt",
|
||||
env=dict(os.environ, SF_PARENT_PID=str(os.getpid())),
|
||||
creationflags=_fx_gui_creationflags())
|
||||
@@ -797,11 +806,13 @@ async def close_fx_gui(req: FxGuiCloseRequest, current_user: dict = Depends(get_
|
||||
|
||||
|
||||
_FX_GUI_PROCESSES = [] # list[subprocess.Popen] — các bridge fx-gui do app spawn
|
||||
_FX_GUI_INFLIGHT = set() # set[str] plugin_path — spawn embed đang chờ verdict
|
||||
|
||||
def _register_fx_gui_process(proc):
|
||||
global _FX_GUI_PROCESSES
|
||||
_prune_fx_gui_processes()
|
||||
_FX_GUI_PROCESSES.append(proc)
|
||||
if proc not in _FX_GUI_PROCESSES:
|
||||
_FX_GUI_PROCESSES.append(proc)
|
||||
|
||||
def _prune_fx_gui_processes():
|
||||
global _FX_GUI_PROCESSES
|
||||
@@ -829,7 +840,7 @@ def _kill_fx_gui_proc(proc):
|
||||
except Exception:
|
||||
proc.kill()
|
||||
|
||||
def _spawn_fx_gui_embed(cmd):
|
||||
def _spawn_fx_gui_embed(cmd, plugin_path=None):
|
||||
"""Spawn the fx-gui bridge and wait for the editor attach verdict.
|
||||
|
||||
Process-level retry: an Ozone-class plugin can crash its GUI thread
|
||||
@@ -851,6 +862,11 @@ def _spawn_fx_gui_embed(cmd):
|
||||
env=dict(os.environ, SF_PARENT_PID=str(os.getpid())),
|
||||
creationflags=_fx_gui_creationflags(),
|
||||
)
|
||||
# Register NGAY sau Popen (trước vòng chờ verdict 20-70s): request thứ 2
|
||||
# cho cùng plugin thấy process đang chạy → không spawn thêm instance GUI.
|
||||
if plugin_path is not None:
|
||||
proc._sf_plugin_path = plugin_path
|
||||
_register_fx_gui_process(proc)
|
||||
holder = {"port": None, "opened": False}
|
||||
|
||||
def _watch(pp, h):
|
||||
|
||||
Reference in New Issue
Block a user