From 6b021916296a7ef33e154546d470b293d8804c0b Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 31 Aug 2026 11:13:35 +0100 Subject: [PATCH] Try to break potential deadlock between UI and replay threads * During capture shutdown on Python < 3.13 the replay thread will try to remove itself as a debuggable thread which requires being able to run python (holding the GIL). If the UI thread is the one triggering the capture shutdown and is running a call from a python script it may hold the GIL while waiting for the replay thread to close, deadlocking. * To alleviate this, while the UI thread is about to block on the replay thread we try to release the python state if we are holding it so that the replay thread can run python code if needed. * We already do this in a manual wrapper around BlockInvoke. --- qrenderdoc/Code/ReplayManager.cpp | 8 ++++++++ qrenderdoc/Code/pyrenderdoc/PythonContext.cpp | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/qrenderdoc/Code/ReplayManager.cpp b/qrenderdoc/Code/ReplayManager.cpp index 83cc1bcca..0a6e30448 100644 --- a/qrenderdoc/Code/ReplayManager.cpp +++ b/qrenderdoc/Code/ReplayManager.cpp @@ -294,8 +294,12 @@ void ReplayManager::BlockInvoke(ReplayManager::ReplayInvokeCallback m) PushInvoke(cmd); + void *ctx = PythonContext::PausePythonThreading(); + cmd->processed.acquire(); + PythonContext::ResumePythonThreading(ctx); + delete cmd; } @@ -315,6 +319,8 @@ void ReplayManager::CloseThread() if(m_Thread == NULL) return; + void *ctx = PythonContext::PausePythonThreading(); + // wait for the thread to close and clean up while(m_Thread->isRunning()) { @@ -322,6 +328,8 @@ void ReplayManager::CloseThread() m_Thread->deleteLater(); m_Thread = NULL; + + PythonContext::ResumePythonThreading(ctx); } ResultDetails ReplayManager::ConnectToRemoteServer(RemoteHost host) diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index 75fc7fff8..fece2c673 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -1146,12 +1146,13 @@ void PythonContext::Finish() void *PythonContext::PausePythonThreading() { - return PyEval_SaveThread(); + return PyGILState_Check() == 0 ? NULL : PyEval_SaveThread(); } void PythonContext::ResumePythonThreading(void *ctx) { - PyEval_RestoreThread((PyThreadState *)ctx); + if(ctx) + PyEval_RestoreThread((PyThreadState *)ctx); } void PythonContext::GlobalShutdown()