// plugins/pyloader/src/python_cpu_policy.cc - Copyright 2005, 2006, University // of Padova, dept. of Pure and Applied // Mathematics // // This file is part of SGPEMv2. // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // SGPEMv2 is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with SGPEMv2; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "config.h" #include "python_cpu_policy.hh" #include #include #include using namespace sgpem; using namespace std; // Static member data Glib::StaticRecMutex PythonCPUPolicy::_mtx = GLIBMM_STATIC_REC_MUTEX_INIT; // WARNING : this class needs extensive and above all // *strong* exception checking / handling! PythonCPUPolicy::PythonCPUPolicy(const char* name) throw(MalformedPolicyException) : _upolicy_dict(NULL), _adapter(NULL), _name(name), _description() { PyObject* pLoadmeStr = PyString_FromString(name); PyObject* pUserCPUPolicyModule = PyImport_Import(pLoadmeStr); Py_DECREF(pLoadmeStr); if (pUserCPUPolicyModule == NULL) throw MalformedPolicyException(get_exception_information()); // Dictionary with defined ``symbols'' for .pyc file _upolicy_dict = PyModule_GetDict(pUserCPUPolicyModule); assert(_upolicy_dict); // Loads ScriptAdapter module and get its dictionary pLoadmeStr = PyString_FromString("ScriptAdapter"); PyObject* pScriptAdapterModule = PyImport_Import(pLoadmeStr); Py_DECREF(pLoadmeStr); assert(pScriptAdapterModule); PyObject* pAdapterDict = PyModule_GetDict(pScriptAdapterModule); assert(pAdapterDict); // Now takes the user-defined policy class from pUserCPUPolicyDict PyObject* pCPUPolicyClass = PyDict_GetItemString(_upolicy_dict, name); if (pCPUPolicyClass == NULL) throw MalformedPolicyException(Glib::ustring(_("Cannot find a class named ")) + name + _(" into the corresponding .py file.")); // Retrieve a description for the policy using the __doc__ attribute PyObject* pDescriptionString = PyObject_GetAttrString(pCPUPolicyClass, "__doc__"); if(pDescriptionString != Py_None) _description = PyString_AsString(pDescriptionString); Py_DECREF(pDescriptionString); // Creates a new object of type ScriptAdapter : // takes init function from ScriptAdapter class PyObject* pAdapterClass = PyDict_GetItemString(pAdapterDict, "ScriptAdapter"); PyObject* pAdapterCtorParam = PyTuple_New(1); Py_INCREF(pCPUPolicyClass); // PyTuple_SetItem steals a reference PyTuple_SetItem(pAdapterCtorParam, 0, pCPUPolicyClass); _adapter = PyInstance_New(pAdapterClass, pAdapterCtorParam, NULL); Py_DECREF(pAdapterCtorParam); Py_DECREF(pScriptAdapterModule); if (_adapter == NULL) throw MalformedPolicyException(get_exception_information()); // And now, who's your daddy, huh? } PythonCPUPolicy::~PythonCPUPolicy() { if (_adapter) Py_DECREF(_adapter); // We keep this alive until dtor time, because // the user may have defined some static global-space // variables and they make use of them. if (_upolicy_dict) Py_DECREF(_upolicy_dict); } void PythonCPUPolicy::activate() throw(UserInterruptException, MalformedPolicyException) { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); // TODO write the rest, taking away code from constructor configure(); set_callback_policy(NULL); } void PythonCPUPolicy::deactivate() { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); // TODO See if some code has to be moved here from the // destructor, AFTER having written activate() _parameters.clear(); set_callback_policy(NULL); } void PythonCPUPolicy::configure() throw(UserInterruptException, MalformedPolicyException) { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); PyObject* retval = PyObject_CallMethod(_adapter, "async_configure", NULL); Py_DECREF(retval); wait_unlock(); set_callback_policy(NULL); } void PythonCPUPolicy::sort_queue() const throw(UserInterruptException, MalformedPolicyException) { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); PyObject* retval = PyObject_CallMethod(_adapter, "async_sort_queue", NULL); // Do minimal debugging if (!retval) PyErr_Print(); else Py_DECREF(retval); wait_unlock(); set_callback_policy(NULL); } Glib::ustring PythonCPUPolicy::get_description() const { return _description; } Glib::ustring PythonCPUPolicy::get_name() const { return _name; } bool PythonCPUPolicy::is_pre_emptive() const throw(UserInterruptException, MalformedPolicyException) { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); PyObject* retval = PyObject_CallMethod(_adapter, "async_is_preemptive", NULL); Py_DECREF(retval); wait_unlock(); // Parse return value stored in global Python object retval = PyObject_CallMethod(_adapter, "get_return_value", NULL); assert(retval); bool ret = PyObject_IsTrue(retval); Py_DECREF(retval); set_callback_policy(NULL); return ret; } int PythonCPUPolicy::get_time_slice() const throw(UserInterruptException, MalformedPolicyException) { Glib::RecMutex::Lock lock(_mtx);; set_callback_policy(const_cast(this)); PyObject* retval = PyObject_CallMethod(_adapter, "async_get_time_slice", NULL); // Do minimal debugging if (!retval) PyErr_Print(); else Py_DECREF(retval); wait_unlock(); // Parse return value stored in global Python object retval = PyObject_CallMethod(_adapter, "get_return_value", NULL); assert(retval); long tmp = PyInt_AsLong(retval); Py_DECREF(retval); set_callback_policy(NULL); return tmp < 0 ? numeric_limits::max() : static_cast(tmp); } void PythonCPUPolicy::wait_unlock() const throw(UserInterruptException, MalformedPolicyException) { // Polling time static const int wait_for = 150000; PyThreadState* _save; int i = 0; // We give the sort_queue() three seconds max time, then... // we shot it stone dead! Bang. bool still_locked; do { Py_UNBLOCK_THREADS; Glib::usleep(wait_for); // hack'a'ton! magggggiccc nummmbeeerrrrrs!! Py_BLOCK_THREADS; PyObject* retval = PyObject_CallMethod(_adapter, "mutex_test_lock", NULL); assert(retval); still_locked = PyObject_IsTrue(retval); Py_DECREF(retval); if (i++ > (1000000 / wait_for) * 5) /* waits for ~5 seconds */ { PyThreadState_Clear(_save); // As the API documentation says, the caller of PyEval_RestoreThread // should NOT possess the interpreter lock Py_UNBLOCK_THREADS; PyEval_RestoreThread(_save); if(PyErr_Occurred() != NULL) abort(); throw UserInterruptException(_("User-defined policy is " "taking too long to terminate.")); } } while (still_locked); // check if there were unhandled exception in the user-defined code PyObject* pException = PyObject_CallMethod(_adapter, "get_last_exception", NULL); if(pException != NULL) { if(pException != Py_None) { PyObject* pExceptStr = PyObject_Str(pException); string msg = _("unhandled exception in user policy: "); msg += PyString_AsString(pExceptStr); Py_DECREF(pExceptStr); Py_DECREF(pException); throw MalformedPolicyException(msg); } else Py_DECREF(pException); } // What we should really do here: /* do { enable python threads wait for some time... disable python threads ...check if user asked for interruption, reading a syncronized variable... ...if he has, break ...else: if the global lock is set: stay in this loop else: all's went okay, can exit loop } */ } string PythonCPUPolicy::get_exception_information() { if (PyErr_Occurred() == NULL) return _("no error"); PyObject* pType = NULL; PyObject* pValue = NULL; PyObject* pTraceback = NULL; PyErr_Fetch(&pType, &pValue, &pTraceback); string msg; if (pValue != NULL) { PyObject* pValueStr = PyObject_Str(pValue); msg = PyString_AsString(pValueStr); Py_DECREF(pValueStr); } else msg = string(_("no available information for this error")); if (pType != NULL) Py_DECREF(pType); if (pValue != NULL) Py_DECREF(pValue); if (pTraceback != NULL) Py_DECREF(pTraceback); PyErr_Clear(); return msg; }