1 // Copyright (C) 2019 EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 // Author: Anthony Geay, anthony.geay@edf.fr, EDF R&D
21 #include "AdaoExchangeLayer4Quintet.hxx"
22 #include "AdaoExchangeLayerException.hxx"
23 #include "AdaoModelKeyVal.hxx"
24 #include "PyObjectRAII.hxx"
27 #include "py2cpp/py2cpp.hxx"
29 #include <semaphore.h>
38 struct DataExchangedBetweenThreads // data written by subthread and read by calling thread
41 DataExchangedBetweenThreads();
42 ~DataExchangedBetweenThreads();
45 sem_t _sem_result_is_here;
46 volatile bool _finished = false;
47 volatile PyObject *_data = nullptr;
50 /////////////////////////////////////////////
55 DataExchangedBetweenThreads *_data;
58 static PyObject *adaocallback_call(AdaoCallbackSt *self, PyObject *args, PyObject *kw)
60 if(!PyTuple_Check(args))
61 throw AdaoExchangeLayerException("Input args is not a tuple as expected !");
62 if(PyTuple_Size(args)!=1)
63 throw AdaoExchangeLayerException("Input args is not a tuple of size 1 as expected !");
64 PyObjectRAII zeobj(PyObjectRAII::FromBorrowed(PyTuple_GetItem(args,0)));
66 throw AdaoExchangeLayerException("Retrieve of elt #0 of input tuple has failed !");
67 volatile PyObject *ret(nullptr);
68 PyThreadState *tstate(PyEval_SaveThread());// GIL is acquired (see ExecuteAsync). Before entering into non python section. Release lock
70 self->_data->_finished = false;
71 self->_data->_data = zeobj;
72 sem_post(&self->_data->_sem);
73 sem_wait(&self->_data->_sem_result_is_here);
74 ret = self->_data->_data;
76 PyEval_RestoreThread(tstate);//End of parallel section. Reaquire the GIL and restore the thread state
77 return (PyObject *)ret;
80 static int adaocallback___init__(PyObject *self, PyObject *args, PyObject *kwargs) { return 0; }
82 static PyObject *adaocallback___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
84 return (PyObject *)( type->tp_alloc(type, 0) );
87 static void adaocallback_dealloc(PyObject *self)
89 Py_TYPE(self)->tp_free(self);
92 PyTypeObject AdaoCallbackType = {
93 PyVarObject_HEAD_INIT(&PyType_Type, 0)
95 sizeof(AdaoCallbackSt),
97 adaocallback_dealloc, /*tp_dealloc*/
104 0, /*tp_as_sequence*/
107 (ternaryfunc)adaocallback_call, /*tp_call*/
112 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
116 0, /*tp_richcompare*/
117 0, /*tp_weaklistoffset*/
128 adaocallback___init__, /*tp_init*/
129 PyType_GenericAlloc, /*tp_alloc*/
130 adaocallback___new__, /*tp_new*/
131 PyObject_GC_Del, /*tp_free*/
134 /////////////////////////////////////////////
136 DataExchangedBetweenThreads::DataExchangedBetweenThreads()
138 if(sem_init(&_sem,0,0)!=0)// put value to 0 to lock by default
139 throw AdaoExchangeLayerException("Internal constructor : Error on initialization of semaphore !");
140 if(sem_init(&_sem_result_is_here,0,0)!=0)// put value to 0 to lock by default
141 throw AdaoExchangeLayerException("Internal constructor : Error on initialization of semaphore !");
144 DataExchangedBetweenThreads::~DataExchangedBetweenThreads()
147 sem_destroy(&_sem_result_is_here);
150 class AdaoCallbackKeeper
153 void assign(AdaoCallbackSt *pt, DataExchangedBetweenThreads *data)
159 PyObject *getPyObject() const { return reinterpret_cast<PyObject*>(_pt); }
160 ~AdaoCallbackKeeper() { release(); }
162 void release() { if(_pt) { Py_XDECREF(_pt); } }
164 AdaoCallbackSt *_pt = nullptr;
167 class AdaoExchangeLayer4Quintet::Internal
170 Internal():_context(PyObjectRAII::FromNew(PyDict_New()))
172 PyObject *mainmod(PyImport_AddModule("__main__"));
173 PyObject *globals(PyModule_GetDict(mainmod));
174 PyObject *bltins(PyEval_GetBuiltins());
175 PyDict_SetItemString(_context,"__builtins__",bltins);
178 PyObjectRAII _context;
179 PyObjectRAII _generate_case_func;
180 PyObjectRAII _decorator_func;
181 PyObjectRAII _adao_case;
182 PyObjectRAII _execute_func;
183 AdaoCallbackKeeper _py_call_back;
184 std::future< void > _fut;
185 PyThreadState *_tstate = nullptr;
186 DataExchangedBetweenThreads _data_btw_threads;
189 wchar_t **ConvertToWChar(int argc, const char *argv[])
191 wchar_t **ret(new wchar_t*[argc]);
192 for(int i=0;i<argc;++i)
194 std::size_t len(strlen(argv[i])+1);
195 wchar_t *elt(new wchar_t[len]);
197 std::mbstowcs(elt, argv[i], len);
202 void FreeWChar(int argc, wchar_t **tab)
204 for(int i=0;i<argc;++i)
209 AdaoExchangeLayer4Quintet::AdaoExchangeLayer4Quintet()
213 AdaoExchangeLayer4Quintet::~AdaoExchangeLayer4Quintet()
218 void AdaoExchangeLayer4Quintet::init(AdaoModel::MainModel *model)
220 initPythonIfNeeded();
224 void AdaoExchangeLayer4Quintet::initPythonIfNeeded()
226 if (!Py_IsInitialized())
228 const char *TAB[]={"AdaoExchangeLayer4Quintet"};
229 wchar_t **TABW(ConvertToWChar(1,TAB));
230 // Python is not initialized
231 Py_SetProgramName(const_cast<wchar_t *>(TABW[0]));
232 Py_Initialize(); // Initialize the interpreter
233 PySys_SetArgv(1,TABW);
235 PyEval_InitThreads();
238 _internal = new Internal;
241 class Visitor1 : public AdaoModel::PythonLeafVisitor
244 Visitor1(PyObjectRAII func, PyObject *context):_func(func),_context(context)
246 std::vector< std::vector<double> > bounds{ {0., 10.}, {3., 13.}, {1.5, 15.5} };
247 std::vector< double > Xb{5.,7.,9.};
248 py2cpp::PyPtr boundsPy(py2cpp::toPyPtr(bounds));
249 _bounds = boundsPy.get();
251 py2cpp::PyPtr XbPy(py2cpp::toPyPtr(Xb));
254 std::vector<double> observation{2., 6., 12., 20.};
255 py2cpp::PyPtr observationPy(py2cpp::toPyPtr(observation));
256 _observation = observationPy.get();
257 Py_XINCREF(_observation);
260 void visit(AdaoModel::MainModel *godFather, AdaoModel::PyObjKeyVal *obj) override
262 if(obj->getKey()=="Matrix" || obj->getKey()=="DiagonalSparseMatrix")
264 std::ostringstream oss; oss << "__" << _cnt++;
265 std::string varname(oss.str());
266 obj->setVal(Py_None);
267 PyDict_SetItemString(_context,varname.c_str(),Py_None);
268 obj->setVarName(varname);
271 if(obj->getKey()=="Bounds")
273 std::ostringstream oss; oss << "__" << _cnt++;
274 std::string varname(oss.str());
275 obj->setVal(_bounds);
276 PyDict_SetItemString(_context,varname.c_str(),_bounds);
277 obj->setVarName(varname);
280 if(godFather->findPathOf(obj)=="Background/Vector")
282 std::ostringstream oss; oss << "__" << _cnt++;
283 std::string varname(oss.str());
285 PyDict_SetItemString(_context,varname.c_str(),_Xb);
286 obj->setVarName(varname);
288 if(obj->getKey()=="OneFunction")
290 std::ostringstream oss; oss << "__" << _cnt++;
291 std::string varname(oss.str());
293 PyDict_SetItemString(_context,varname.c_str(),_func);
294 obj->setVarName(varname);
297 if(godFather->findPathOf(obj)=="Observation/Vector")
299 std::ostringstream oss; oss << "__" << _cnt++;
300 std::string varname(oss.str());
301 obj->setVal(_observation);
302 PyDict_SetItemString(_context,varname.c_str(),_observation);
303 obj->setVarName(varname);
307 unsigned int _cnt = 0;
308 PyObject *_bounds = nullptr;
309 PyObject *_Xb = nullptr;
310 PyObject *_observation = nullptr;
315 void AdaoExchangeLayer4Quintet::loadTemplate(AdaoModel::MainModel *model)
317 const char DECORATOR_FUNC[]="def DecoratorAdao(cppFunc):\n"
318 " def evaluator( xserie ):\n"
319 " import numpy as np\n"
320 " yserie = [np.array(elt) for elt in cppFunc(xserie)]\n"
322 " return evaluator\n";
323 this->_internal->_py_call_back.assign(PyObject_GC_New(AdaoCallbackSt,&AdaoCallbackType),
324 &this->_internal->_data_btw_threads);
325 PyObject *callbackPyObj(this->_internal->_py_call_back.getPyObject());
328 PyObjectRAII res(PyObjectRAII::FromNew(PyRun_String(DECORATOR_FUNC,Py_file_input,this->_internal->_context,this->_internal->_context)));
329 PyObjectRAII decoratorGenerator( PyObjectRAII::FromBorrowed(PyDict_GetItemString(this->_internal->_context,"DecoratorAdao")) );
330 if(decoratorGenerator.isNull())
331 throw AdaoExchangeLayerException("Fail to locate DecoratorAdao function !");
332 PyObjectRAII args(PyObjectRAII::FromNew(PyTuple_New(1)));
333 { PyTuple_SetItem(args,0,callbackPyObj); Py_XINCREF(callbackPyObj); }
334 this->_internal->_decorator_func = PyObjectRAII::FromNew(PyObject_CallObject(decoratorGenerator,args));
335 if(this->_internal->_decorator_func.isNull())
336 throw AdaoExchangeLayerException("Fail to generate result of DecoratorAdao function !");
339 Visitor1 visitor(this->_internal->_decorator_func,this->_internal->_context);
340 model->visitPythonLeaves(&visitor);
343 std::string sciptPyOfModelMaker(model->pyStr());
344 PyObjectRAII res(PyObjectRAII::FromNew(PyRun_String(sciptPyOfModelMaker.c_str(),Py_file_input,this->_internal->_context,this->_internal->_context)));
345 _internal->_adao_case = PyObjectRAII::FromNew(PyDict_GetItemString(this->_internal->_context,"case"));
347 if(_internal->_adao_case.isNull())
348 throw AdaoExchangeLayerException("Fail to generate ADAO case object !");
350 _internal->_execute_func=PyObjectRAII::FromNew(PyObject_GetAttrString(_internal->_adao_case,"execute"));
351 if(_internal->_execute_func.isNull())
352 throw AdaoExchangeLayerException("Fail to locate execute function of ADAO case object !");
355 void ExecuteAsync(PyObject *pyExecuteFunction, DataExchangedBetweenThreads *data)
357 AutoGIL gil; // launched in a separed thread -> protect python calls
358 PyObjectRAII args(PyObjectRAII::FromNew(PyTuple_New(0)));
359 PyObjectRAII nullRes(PyObjectRAII::FromNew(PyObject_CallObject(pyExecuteFunction,args)));// go to adaocallback_call
361 data->_finished = true;
362 data->_data = nullptr;
363 sem_post(&data->_sem);
366 void AdaoExchangeLayer4Quintet::execute()
368 _internal->_tstate=PyEval_SaveThread(); // release the lock acquired in AdaoExchangeLayer4Quintet::initPythonIfNeeded by PyEval_InitThreads()
369 _internal->_fut = std::async(std::launch::async,ExecuteAsync,_internal->_execute_func,&_internal->_data_btw_threads);
372 bool AdaoExchangeLayer4Quintet::next(PyObject *& inputRequested)
374 sem_wait(&_internal->_data_btw_threads._sem);
375 if(_internal->_data_btw_threads._finished)
377 inputRequested = nullptr;
382 inputRequested = (PyObject *)_internal->_data_btw_threads._data;
387 void AdaoExchangeLayer4Quintet::setResult(PyObject *outputAssociated)
389 _internal->_data_btw_threads._data = outputAssociated;
390 _internal->_data_btw_threads._finished = false;
391 sem_post(&_internal->_data_btw_threads._sem_result_is_here);
394 PyObject *AdaoExchangeLayer4Quintet::getResult()
396 _internal->_fut.wait();
397 PyEval_RestoreThread(_internal->_tstate);
399 // now retrieve case.get("Analysis")[-1]
400 PyObjectRAII get_func_of_adao_case(PyObjectRAII::FromNew(PyObject_GetAttrString(_internal->_adao_case,"get")));
401 if(get_func_of_adao_case.isNull())
402 throw AdaoExchangeLayerException("Fail to locate \"get\" method from ADAO case !");
403 PyObjectRAII all_intermediate_results;
404 {// retrieve return data from case.get("Analysis")
405 PyObjectRAII args(PyObjectRAII::FromNew(PyTuple_New(1)));
406 PyTuple_SetItem(args,0,PyUnicode_FromString("Analysis"));
407 all_intermediate_results=PyObjectRAII::FromNew(PyObject_CallObject(get_func_of_adao_case,args));
408 if(all_intermediate_results.isNull())
409 throw AdaoExchangeLayerException("Fail to retrieve result of case.get(\"Analysis\") !");
411 PyObjectRAII optimum;
413 PyObjectRAII param(PyObjectRAII::FromNew(PyLong_FromLong(-1)));
414 optimum=PyObjectRAII::FromNew(PyObject_GetItem(all_intermediate_results,param));
416 throw AdaoExchangeLayerException("Fail to retrieve result of last element of case.get(\"Analysis\") !");
418 /*PyObjectRAII code(PyObjectRAII::FromNew(Py_CompileString("case.get(\"Analysis\")[-1]","retrieve result",Py_file_input)));
420 throw AdaoExchangeLayerException("Fail to compile code to retrieve result after ADAO computation !");
421 PyObjectRAII res(PyObjectRAII::FromNew(PyEval_EvalCode(code,_internal->_context,_internal->_context)));*/
422 return optimum.retn();