diff --git a/plugins/gui/include/gui/python/python_thread.h b/plugins/gui/include/gui/python/python_thread.h index c3d867f797b..977ddbf19ec 100644 --- a/plugins/gui/include/gui/python/python_thread.h +++ b/plugins/gui/include/gui/python/python_thread.h @@ -35,6 +35,26 @@ namespace hal { class Module; class Gate; + /** + * Class to aquire and release GIL + * + * It is important that the GIL is not released before + * Python code has removed all objects created during + * execution. Therefore the GIL release is done at the + * very end when running out of scope and calling the + * destructor + */ + class PythonMutex + { + int mState; + public: + /// Aquire GIL + PythonMutex(); + + /// Release GIL + ~PythonMutex(); + }; + class PythonThread : public QThread, public PythonContextSubscriber { public: diff --git a/plugins/gui/src/python/python_thread.cpp b/plugins/gui/src/python/python_thread.cpp index c6b4bf28d65..d894ada5f75 100644 --- a/plugins/gui/src/python/python_thread.cpp +++ b/plugins/gui/src/python/python_thread.cpp @@ -11,6 +11,16 @@ #include namespace hal { + PythonMutex::PythonMutex() + { + mState = PyGILState_Ensure(); + } + + PythonMutex::~PythonMutex() + { + PyGILState_Release((PyGILState_STATE)mState); + } + PythonThread::PythonThread(const QString& script, bool singleStatement, QObject* parent) : QThread(parent), mScript(script), mSingleStatement(singleStatement), mAbortRequested(false), mSpamCount(0) @@ -26,7 +36,7 @@ namespace hal { void PythonThread::run() { // decides it's our turn. - PyGILState_STATE state = PyGILState_Ensure(); + PythonMutex pmutex; py::dict tmp_context(py::globals()); PythonContext::initializeScript(&tmp_context); @@ -60,7 +70,7 @@ namespace hal { mErrorMessage = QString::fromStdString(std::string(e.what()) + "#\n"); } - PyGILState_Release(state); + // running out of scope calls PyGILState_Release(state); } void PythonThread::handleStdout(const QString& output) @@ -114,7 +124,7 @@ namespace hal { mInputMutex.unlock(); } qDebug() << "about to terminate thread..." << mPythonThreadID; - PyGILState_STATE state = PyGILState_Ensure(); + PythonMutex pmutex; // We interrupt the thread by forcibly injecting an exception // (namely the KeyboardInterrupt exception, but any other one works as well) int nThreads = PyThreadState_SetAsyncExc(mPythonThreadID, PyExc_KeyboardInterrupt); @@ -127,8 +137,8 @@ namespace hal { // apparently this can actually happen if you mess up the C<->Python bindings qDebug() << "Oh no! There seem to be multiple threads with the same ID!"; } - PyGILState_Release(state); qDebug() << "thread terminated"; + // running out of scope calls PyGILState_Release(state); } bool PythonThread::getInput(InputType type, QString prompt, QVariant defaultValue)