From 3c540abbae15511d6645fcd0b8f498c996f95c4e Mon Sep 17 00:00:00 2001 From: Emmanuel Durand Date: Thu, 28 Jul 2016 17:02:36 -0400 Subject: [PATCH] Improved thread safety in PythonEmbedded --- addons/python/repl.py | 33 +++++++++++++++++++++++++++++ include/controller_pythonEmbedded.h | 1 + src/controller_pythonEmbedded.cpp | 13 +++++++----- 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 addons/python/repl.py diff --git a/addons/python/repl.py b/addons/python/repl.py new file mode 100644 index 00000000..b6dc822a --- /dev/null +++ b/addons/python/repl.py @@ -0,0 +1,33 @@ +# This script should be loaded inside a python object of Splash. +# To do so, add the following to one of a scene configuration: +# +# "python" : { +# "type" : "python", +# "file" : "./httpServer.py" +# } +# +# For this to work, the configuration file should be in the same +# directory as this script. Otherwise, modify the path accordingly + +import splash +import code +import threading + +def repl(): + import splash + global console + console = code.InteractiveConsole(locals=locals()) + console.interact() + +replThread = threading.Thread(target=repl) + +def splash_init(): + replThread.start() + +def splash_loop(): + pass + +def splash_stop(): + print("Press a key to quit") + console.push("quit()") + pass diff --git a/include/controller_pythonEmbedded.h b/include/controller_pythonEmbedded.h index 3e26f92b..40aeca90 100644 --- a/include/controller_pythonEmbedded.h +++ b/include/controller_pythonEmbedded.h @@ -74,6 +74,7 @@ class PythonEmbedded : public ControllerObject std::string _filepath {""}; //!< Path to the python script std::string _scriptName {""}; //!< Name of the module (filename minus .py) + PyThreadState* _pyThreadState {nullptr}; bool _doLoop {false}; //!< Set to false to stop the Python loop int _loopDurationMs {5}; //!< Time between loops in ms std::thread _loopThread {}; //!< Python thread loop diff --git a/src/controller_pythonEmbedded.cpp b/src/controller_pythonEmbedded.cpp index 27282563..add37fe8 100644 --- a/src/controller_pythonEmbedded.cpp +++ b/src/controller_pythonEmbedded.cpp @@ -132,7 +132,6 @@ PyObject* PythonEmbedded::pythonSetGlobal(PyObject* self, PyObject* args) } auto value = convertToValue(pyValue).asValues(); - Py_XDECREF(pyValue); that->setGlobal(string(strName), value); Py_INCREF(Py_True); @@ -159,7 +158,6 @@ PyObject* PythonEmbedded::pythonSetObject(PyObject* self, PyObject* args) } auto value = convertToValue(pyValue).asValues(); - Py_XDECREF(pyValue); that->setObject(string(strName), string(strAttr), value); Py_INCREF(Py_True); @@ -186,7 +184,6 @@ PyObject* PythonEmbedded::pythonSetObjectsOfType(PyObject* self, PyObject* args) } auto value = convertToValue(pyValue).asValues(); - Py_XDECREF(pyValue); that->setObjectsOfType(string(strType), string(strAttr), value); Py_INCREF(Py_True); @@ -325,7 +322,7 @@ bool PythonEmbedded::run() /*************/ bool PythonEmbedded::stop() { - // Tell // Stop and wait for the loop + // Stop and wait for the loop _doLoop = false; if (_loopThread.joinable()) _loopThread.join(); @@ -346,6 +343,7 @@ void PythonEmbedded::loop() // Load the module by its filename Py_Initialize(); + PyEval_InitThreads(); PyRun_SimpleString("import sys"); PyRun_SimpleString(("sys.path.append(\"" + _filepath + "\")").c_str()); @@ -369,15 +367,20 @@ void PythonEmbedded::loop() auto pFuncLoop = getFuncFromModule(pModule, "splash_loop"); auto timerName = "PythonEmbedded_" + _name; + + _pyThreadState = PyEval_SaveThread(); while (pFuncLoop && _doLoop) { Timer::get() << timerName; + PyEval_RestoreThread(_pyThreadState); PyObject_CallObject(pFuncLoop, nullptr); if (PyErr_Occurred()) PyErr_Print(); + _pyThreadState = PyEval_SaveThread(); Timer::get() >> _loopDurationMs * 1000 >> timerName; } Py_XDECREF(pFuncLoop); + PyEval_RestoreThread(_pyThreadState); auto pFuncStop = getFuncFromModule(pModule, "splash_stop"); if (pFuncStop) @@ -409,7 +412,7 @@ PyObject* PythonEmbedded::getFuncFromModule(PyObject* module, const string& name { if (PyErr_Occurred()) PyErr_Print(); - Log::get() << Log::WARNING << "PythonEmbedded::" << __FUNCTION__ << " - Cannot find function " << _scriptName << Log::endl; + Log::get() << Log::WARNING << "PythonEmbedded::" << __FUNCTION__ << " - Cannot find function " << name << Log::endl; } return pFunc;