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.
This commit is contained in:
baldurk
2026-08-31 11:13:35 +01:00
parent c98181bb9f
commit 6b02191629
2 changed files with 11 additions and 2 deletions
+8
View File
@@ -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)
@@ -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()