// Filename: pythonThread.cxx // Created by: drose (13Apr07) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "pythonThread.h" #include "pnotify.h" #ifdef HAVE_PYTHON TypeHandle PythonThread::_type_handle; //////////////////////////////////////////////////////////////////// // Function: PythonThread::Constructor // Access: Published // Description: //////////////////////////////////////////////////////////////////// PythonThread:: PythonThread(PyObject *function, PyObject *args, const string &name, const string &sync_name) : Thread(name, sync_name) { _function = function; Py_INCREF(_function); _args = NULL; _result = NULL; if (!PyCallable_Check(_function)) { nassert_raise("Invalid function passed to PythonThread constructor"); } if (args == Py_None) { // None means no arguments; create an empty tuple. _args = PyTuple_New(0); } else { _args = NULL; if (PySequence_Check(args)) { _args = PySequence_Tuple(args); } if (_args == NULL) { nassert_raise("Invalid args passed to PythonThread constructor"); } } #ifndef SIMPLE_THREADS // Ensure that the Python threading system is initialized and ready // to go. PyEval_InitThreads(); #endif } //////////////////////////////////////////////////////////////////// // Function: PythonThread::Destructor // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// PythonThread:: ~PythonThread() { Py_DECREF(_function); Py_XDECREF(_args); Py_XDECREF(_result); } //////////////////////////////////////////////////////////////////// // Function: PythonThread::join // Access: Published // Description: Blocks the calling process until the thread // terminates. If the thread has already terminated, // this returns immediately. // // The PythonThread flavor of this function returns the // same value returned by the thread function. //////////////////////////////////////////////////////////////////// PyObject *PythonThread:: join() { Thread::join(); if (_result == NULL) { // No result; return None. return Py_BuildValue(""); } Py_INCREF(_result); return _result; } //////////////////////////////////////////////////////////////////// // Function: PythonThread::thread_main // Access: Protected, Virtual // Description: //////////////////////////////////////////////////////////////////// void PythonThread:: thread_main() { // Create a new Python thread state data structure, so Python can // properly lock itself. #ifdef SIMPLE_THREADS // We can't use the PyGILState interface, which assumes we are using // true OS-level threading (and we might be just using // SIMPLE_THREADS). PyGILState enforces policies like only one // thread state per OS-level thread, which is not true in the case // of SIMPLE_THREADS. PyThreadState *orig_thread_state = PyThreadState_Get(); PyInterpreterState *istate = orig_thread_state->interp; PyThreadState *new_thread_state = PyThreadState_New(istate); PyThreadState_Swap(new_thread_state); // Call the user's function. _result = PyObject_Call(_function, _args, NULL); if (_result == (PyObject *)NULL && PyErr_Occurred()) { handle_python_exception(); } PyThreadState_Swap(orig_thread_state); PyThreadState_Clear(new_thread_state); PyThreadState_Delete(new_thread_state); #else // SIMPLE_THREADS // With true threading enabled, we're better off using PyGILSTate. PyGILState_STATE gstate; gstate = PyGILState_Ensure(); // Call the user's function. _result = PyObject_Call(_function, _args, NULL); if (_result == (PyObject *)NULL && PyErr_Occurred()) { handle_python_exception(); } // Release the thread state data structure. PyGILState_Release(gstate); #endif // SIMPLE_THREADS } //////////////////////////////////////////////////////////////////// // Function: PythonThread::handle_python_exception // Access: Private // Description: Called when a Python exception is raised during // processing of a thread. Gets the error string and // passes it back to the calling Python process in a // sensible way. //////////////////////////////////////////////////////////////////// void PythonThread:: handle_python_exception() { PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); ostringstream strm; strm << "\n"; if (PyObject_HasAttrString(exc, "__name__")) { PyObject *exc_name = PyObject_GetAttrString(exc, "__name__"); PyObject *exc_str = PyObject_Str(exc_name); strm << PyString_AsString(exc_str); Py_DECREF(exc_str); Py_DECREF(exc_name); } else { PyObject *exc_str = PyObject_Str(exc); strm << PyString_AsString(exc_str); Py_DECREF(exc_str); } Py_DECREF(exc); if (val != (PyObject *)NULL) { PyObject *val_str = PyObject_Str(val); strm << ": " << PyString_AsString(val_str); Py_DECREF(val_str); Py_DECREF(val); } if (tb != (PyObject *)NULL) { Py_DECREF(tb); } strm << "\nException occurred within thread " << get_name(); string message = strm.str(); nout << message << "\n"; nassert_raise(message); // Now attempt to force the main thread to the head of the ready // queue, so it will be the one to receive the above assertion. // This mainly only has an effect if SIMPLE_THREADS is in use. Thread::get_main_thread()->preempt(); } #endif // HAVE_PYTHON