332 lines
8.7 KiB
C++
332 lines
8.7 KiB
C++
// 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 "python_cpu_policy.hh"
|
|
|
|
#include <limits>
|
|
#include <unistd.h>
|
|
#include <iostream>
|
|
|
|
using namespace sgpem;
|
|
using namespace std;
|
|
|
|
#define WAIT_FOR (250000)
|
|
|
|
|
|
// 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);
|
|
assert(pCPUPolicyClass); // FIXME needs stricter checking and exception throwing
|
|
|
|
// 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<PythonCPUPolicy*>(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<PythonCPUPolicy*>(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<PythonCPUPolicy*>(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<PythonCPUPolicy*>(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<PythonCPUPolicy*>(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<PythonCPUPolicy*>(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<int>::max() : static_cast<int>(tmp);
|
|
}
|
|
|
|
|
|
void
|
|
PythonCPUPolicy::wait_unlock() const throw(UserInterruptException, MalformedPolicyException)
|
|
{
|
|
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;
|
|
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++ > 12) /* waits for WAIT_FOR * 12 microseconds == 3 secs */
|
|
{
|
|
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;
|
|
}
|
|
|