diff --git a/src/lib/app/PyTwkApp/PyMuSymbolType.cpp b/src/lib/app/PyTwkApp/PyMuSymbolType.cpp index 2439e55e7..1dd7ecbb4 100644 --- a/src/lib/app/PyTwkApp/PyMuSymbolType.cpp +++ b/src/lib/app/PyTwkApp/PyMuSymbolType.cpp @@ -45,14 +45,37 @@ #include #include #include +#include #include #include #include +#include namespace TwkApp { using namespace std; + // Thread safety tracking + // Default constructor creates "not-a-thread" ID + static boost::thread::id s_mainThreadId; + + // Helper function for direct cout printing + static PyObject* unsafe_mu_print(PyObject* args) + { + size_t nargs = PyTuple_Size(args); + if (nargs >= 1) + { + PyObject* arg = PyTuple_GetItem(args, 0); + if (PyUnicode_Check(arg)) + { + const char* str = PyUnicode_AsUTF8(arg); + if (str) + cout << str; + } + } + Py_RETURN_NONE; + } + Mu::FunctionObject* createFunctionObjectFromPyObject(const Mu::FunctionType* t, PyObject* pyobj) { @@ -300,6 +323,33 @@ namespace TwkApp return NULL; } + // Thread safety check - initialize main thread ID on first call + boost::thread::id currentThreadId = boost::this_thread::get_id(); + if (s_mainThreadId == boost::thread::id()) // Check if uninitialized + // (default constructed) + { + s_mainThreadId = currentThreadId; + } + + if (currentThreadId != s_mainThreadId) + { + // fix mu print commands from python to at least not crash from + // non-main thread + // (because python print is often used to debug python code, so + // we'll tolerate this because print is likely redirected to the RV + // console) + if (self->function->fullyQualifiedName() == "extra_commands._print") + { + return unsafe_mu_print(args); + } + + // this cout will probably get redirected to the RV console, or go + // to the terminal window. + cout << "WARNING: Mu " << self->function->fullyQualifiedName() + << "() called from non-main thread, will eventually crash " + << "(Mu isn't thread-safe)." << endl; + } + size_t nargs = PyTuple_Size(args); Mu::Function::ArgumentVector muargs(self->function->numArgs());