| 224 | } |
| 225 | |
| 226 | bool PythonEventsStop(bool bDestroyInterpreter /*= true*/) |
| 227 | { |
| 228 | if (m_PyInterpreter) |
| 229 | { |
| 230 | PythonEventsInitialized = 0; |
| 231 | m_ModuleInitialized = false; |
| 232 | |
| 233 | if (!bDestroyInterpreter) |
| 234 | { |
| 235 | // Keep the sub-interpreter alive so it can be reused on |
| 236 | // restart. This avoids the Py_EndInterpreter / |
| 237 | // Py_NewInterpreter cycle that crashes on Python 3.13 |
| 238 | // due to stale per-thread GIL state in TLS. |
| 239 | // Do NOT call _log.Log() here: during final shutdown the |
| 240 | // logger's sOnLogMessage signal fires callbacks that may |
| 241 | // already be partially torn down, causing a deadlock on |
| 242 | // lsignal's internal mutex. |
| 243 | return true; |
| 244 | } |
| 245 | |
| 246 | _log.Debug(DEBUG_EVENTSYSTEM, "EventSystem - Python: Restoring event interpreter (%p) for shutdown", m_PyInterpreter); |
| 247 | PyEval_RestoreThread((PyThreadState *)m_PyInterpreter); |
| 248 | if (Plugins::Py_IsInitialized()) |
| 249 | { |
| 250 | _log.Debug(DEBUG_EVENTSYSTEM, "EventSystem - Python: Destroying sub-interpreter (%p)", m_PyInterpreter); |
| 251 | Py_EndInterpreter((PyThreadState *)m_PyInterpreter); |
| 252 | } |
| 253 | m_PyInterpreter = nullptr; |
| 254 | PyThreadState *pMainThread = (PyThreadState *)m_mainworker.m_pluginsystem.PythonThread(); |
| 255 | if (!pMainThread) |
| 256 | { |
| 257 | _log.Log(LOG_STATUS, "EventSystem - Python stopped..."); |
| 258 | return true; |
| 259 | } |
| 260 | _log.Debug(DEBUG_EVENTSYSTEM, "EventSystem - Python: Swapping to main thread (%p) and releasing GIL", pMainThread); |
| 261 | PyThreadState_Swap(pMainThread); |
| 262 | // PyEval_ReleaseLock was removed in Python 3.13 (deprecated since 3.2) |
| 263 | // Fall back to PyEval_SaveThread which also releases the GIL |
| 264 | if (PyEval_ReleaseLock) |
| 265 | PyEval_ReleaseLock(); |
| 266 | else |
| 267 | (void)PyEval_SaveThread(); |
| 268 | _log.Log(LOG_STATUS, "EventSystem - Python stopped..."); |
| 269 | return true; |
| 270 | } |
| 271 | return false; |
| 272 | } |
| 273 | |
| 274 | PyObject *PythonEventsGetModule() |
| 275 | { |
no test coverage detected