Salome HOME
1d30beb7d9e0a18c33e38ab0fe7a995cc95cd92b
[modules/yacs.git] / src / engine / OptimizerLoop.cxx
1 #include "OptimizerLoop.hxx"
2 #include "OutputPort.hxx"
3
4 #include <iostream>
5
6 using namespace YACS::ENGINE;
7 using namespace std;
8
9 const char FakeNodeForOptimizerLoop::NAME[]="thisIsAFakeNode";
10
11 const int OptimizerLoop::NOT_RUNNING_BRANCH_ID=-1973012217;
12
13 const char OptimizerLoop::NAME_OF_FILENAME_INPUT[]="FileNameInitAlg";
14
15 const char OptimizerLoop::NAME_OF_OUT_POOL_INPUT[]="retPortForOutPool";
16
17 OptimizerAlgStandardized::OptimizerAlgStandardized(Pool *pool, OptimizerAlgBase *alg)
18   : OptimizerAlgSync(pool),_algBehind(alg),_threadInCaseOfNotEvent(0)
19 {
20   if(_algBehind)
21     _algBehind->incrRef();
22 }
23
24 OptimizerAlgStandardized::~OptimizerAlgStandardized()
25 {
26   if(_algBehind)
27     _algBehind->decrRef();
28 }
29
30 TypeCode *OptimizerAlgStandardized::getTCForIn() const
31 {
32   return _algBehind->getTCForIn();
33 }
34
35 TypeCode *OptimizerAlgStandardized::getTCForOut() const
36 {
37   return _algBehind->getTCForOut();
38 }
39
40 void OptimizerAlgStandardized::setAlgPointer(OptimizerAlgBaseFactory algFactory)
41 {
42   if(_algBehind)
43     _algBehind->decrRef();
44   _algBehind=algFactory(_pool);
45 }
46
47 void OptimizerAlgStandardized::parseFileToInit(const std::string& fileName)
48 {
49   _algBehind->parseFileToInit(fileName);
50 }
51
52 void OptimizerAlgStandardized::initialize(const Any *input) throw (Exception)
53 {
54   _algBehind->initialize(input);
55 }
56
57 void OptimizerAlgStandardized::takeDecision()
58 {
59   switch(_algBehind->getType())
60     {
61     case EVENT_ORIENTED:
62       {
63         ((OptimizerAlgSync *) _algBehind)->takeDecision();
64         break;
65       }
66     case NOT_EVENT_ORIENTED:
67       {   
68         _condition.notifyOneSync();
69         break;
70       }
71     default:
72       throw Exception("Unrecognized type of algorithm. Only 2 types are available : EVENT_ORIENTED or NOT_EVENT_ORIENTED.");
73     }
74 }
75
76 void OptimizerAlgStandardized::finish()
77 {
78   _algBehind->finish();
79 }
80
81 void OptimizerAlgStandardized::start()
82 {
83   switch(_algBehind->getType())
84     {
85     case EVENT_ORIENTED:
86       {
87         ((OptimizerAlgSync *) _algBehind)->start();
88         break;
89       }
90     case NOT_EVENT_ORIENTED:
91       {
92         void **stackForNewTh= new void* [2];
93         stackForNewTh[0]=(void *) ((OptimizerAlgASync *)(_algBehind));//In case of virtual inheritance
94         stackForNewTh[1]=(void *) &_condition;
95         _threadInCaseOfNotEvent=new ::YACS::BASES::Thread(threadFctForAsync,stackForNewTh);
96         _condition.waitForAWait();
97         break;
98       }
99     default:
100       throw Exception("Unrecognized type of algorithm. Only 2 types are available : EVENT_ORIENTED or NOT_EVENT_ORIENTED.");
101     }
102 }
103
104 void *OptimizerAlgStandardized::threadFctForAsync(void* ownStack)
105 {
106   void **ownStackCst=(void **)ownStack;
107   OptimizerAlgASync *alg=(OptimizerAlgASync *)ownStackCst[0];
108   ::YACS::BASES::DrivenCondition *cond=(::YACS::BASES::DrivenCondition *)ownStackCst[1];
109   delete [] ownStackCst;
110   alg->startToTakeDecision(cond);
111 }
112
113 FakeNodeForOptimizerLoop::FakeNodeForOptimizerLoop(OptimizerLoop *loop, bool normal, unsigned reason):ElementaryNode(NAME),_loop(loop),
114                                                                                                       _normal(normal),
115                                                                                                       _reason(reason)
116 {
117   _state=YACS::TOACTIVATE;
118   _father=_loop->getFather();
119 }
120
121 FakeNodeForOptimizerLoop::FakeNodeForOptimizerLoop(const FakeNodeForOptimizerLoop& other):ElementaryNode(other),_loop(0),
122                                                                                           _normal(false)
123
124 }
125
126 Node *FakeNodeForOptimizerLoop::simpleClone(ComposedNode *father, bool editionOnly) const
127 {
128   return new FakeNodeForOptimizerLoop(*this);
129 }
130
131 void FakeNodeForOptimizerLoop::exForwardFailed()
132 {
133   _loop->exForwardFailed();
134   FakeNodeForOptimizerLoop *normallyThis=_loop->_nodeForSpecialCases;
135   _loop->_nodeForSpecialCases=0;
136   delete normallyThis;
137 }
138
139 void FakeNodeForOptimizerLoop::exForwardFinished()
140 {
141   _loop->exForwardFinished();
142   FakeNodeForOptimizerLoop *normallyThis=_loop->_nodeForSpecialCases;
143   _loop->_nodeForSpecialCases=0;
144   delete normallyThis;
145 }
146
147 void FakeNodeForOptimizerLoop::execute()
148 {
149   if(!_normal)
150     {
151       string what;
152       if(_reason==ALG_WITHOUT_START_CASES)
153         what="Alg initialization of optimizerNode with name "; what+=_loop->getName(); what+=" returns no new case(s) to launch";
154       throw Exception(what);
155     }
156 }
157
158 void FakeNodeForOptimizerLoop::aborted()
159 {
160   _loop->_state=YACS::ERROR;
161 }
162
163 void FakeNodeForOptimizerLoop::finished()
164 {
165   
166 }
167
168 OptimizerLoop::OptimizerLoop(const std::string& name, const std::string& algLibWthOutExt,
169                              const std::string& symbolNameToOptimizerAlgBaseInstanceFactory,
170                              bool algInitOnFile) throw(Exception)
171   try : DynParaLoop(name,Runtime::_tc_string),_loader(algLibWthOutExt),_algInitOnFile(algInitOnFile),
172         _portForInitFile(NAME_OF_FILENAME_INPUT,this,Runtime::_tc_string),
173         _alg(new OptimizerAlgStandardized(&_myPool,0)),_convergenceReachedWithOtherCalc(false),
174         _retPortForOutPool(NAME_OF_OUT_POOL_INPUT,this,Runtime::_tc_string),
175         _nodeForSpecialCases(0),_symbol(symbolNameToOptimizerAlgBaseInstanceFactory)
176 {
177   OptimizerAlgBaseFactory algFactory=(OptimizerAlgBaseFactory)_loader.getHandleOnSymbolWithName(_symbol);
178   if(!algFactory)
179     throw Exception("Problem during library loading.");
180   _alg->setAlgPointer(algFactory);
181   _splittedPort.edSetType(_alg->getTCForIn());
182   _retPortForOutPool.edSetType(_alg->getTCForOut());
183 }
184 catch(Exception& e)
185 {
186 }
187
188 OptimizerLoop::OptimizerLoop(const OptimizerLoop& other, ComposedNode *father, bool editionOnly):
189   DynParaLoop(other,father,editionOnly),_algInitOnFile(other._algInitOnFile),_loader(other._loader.getLibNameWithoutExt()),_convergenceReachedWithOtherCalc(false),
190   _alg(new OptimizerAlgStandardized(&_myPool,0)),_portForInitFile(other._portForInitFile,this),_retPortForOutPool(other._retPortForOutPool,this),_nodeForSpecialCases(0),_symbol(other._symbol)
191 {
192   OptimizerAlgBaseFactory algFactory=(OptimizerAlgBaseFactory)_loader.getHandleOnSymbolWithName(_symbol);
193   _alg->setAlgPointer(algFactory);
194   _splittedPort.edSetType(_alg->getTCForIn());
195   _retPortForOutPool.edSetType(_alg->getTCForOut());
196 }
197
198 OptimizerLoop::~OptimizerLoop()
199 {
200   _alg->decrRef();
201   cleanDynGraph();
202   cleanInterceptors();
203 }
204
205 Node *OptimizerLoop::simpleClone(ComposedNode *father, bool editionOnly) const
206 {
207   return new OptimizerLoop(*this,father,editionOnly);
208 }
209
210 void OptimizerLoop::init(bool start)
211 {
212   DynParaLoop::init(start);
213   _portForInitFile.exInit(start);
214   _convergenceReachedWithOtherCalc=false;
215   cleanDynGraph();
216   cleanInterceptors();
217 }
218
219 void OptimizerLoop::exUpdateState()
220 {
221   if(_state == YACS::DISABLED)
222     return;
223   if(_inGate.exIsReady())
224     {
225       _state=YACS::TOACTIVATE;
226       //internal graph update
227       int i;
228       int nbOfBr=_nbOfBranches.getIntValue();
229       if(nbOfBr==0)
230         {
231           delete _nodeForSpecialCases;
232           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::NO_BRANCHES);
233           return;
234         }
235       
236       if(_portForInitFile.isEmpty())
237         {
238           delete _nodeForSpecialCases;
239           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::NO_ALG_INITIALIZATION);
240           return;
241         }
242       _execNodes.resize(nbOfBr);
243       _execIds.resize(nbOfBr);
244       if(_initNode)
245         {
246           _execInitNodes.resize(nbOfBr);
247           _initNodeUpdated.resize(nbOfBr);
248           for(i=0;i<nbOfBr;i++)
249             _initNodeUpdated[i]=false;
250         }
251       for(i=0;i<nbOfBr;i++)
252         {
253           _execIds[i]=NOT_RUNNING_BRANCH_ID;
254           _execNodes[i]=_node->clone(this,false);
255           if(_initNode)
256             _execInitNodes[i]=_initNode->clone(this,false);
257           prepareInputsFromOutOfScope(i);
258         }
259       initInterceptors(nbOfBr);
260       _alg->parseFileToInit(_portForInitFile.getValue()->getStringValue());
261       _alg->start();
262       int id;
263       unsigned char priority;
264       Any *val=_myPool.getNextSampleWithHighestPriority(id,priority);
265       if(!val)
266         {
267           delete _nodeForSpecialCases;
268           _nodeForSpecialCases=new FakeNodeForOptimizerLoop(this,getAllOutPortsLeavingCurrentScope().empty(),FakeNodeForOptimizerLoop::ALG_WITHOUT_START_CASES);
269           return;
270         }
271       launchMaxOfSamples(true);
272     }
273 }
274
275 int OptimizerLoop::getNumberOfInputPorts() const
276 {
277   return DynParaLoop::getNumberOfInputPorts()+2;
278 }
279
280 InputPort *OptimizerLoop::getInputPort(const std::string& name) const throw(Exception)
281 {
282   if(name==NAME_OF_FILENAME_INPUT)
283     return (InputPort *)&_portForInitFile;
284   else if(name==NAME_OF_OUT_POOL_INPUT)
285     return (InputPort *)&_retPortForOutPool;
286   else
287     return DynParaLoop::getInputPort(name);
288 }
289
290 std::list<InputPort *> OptimizerLoop::getSetOfInputPort() const
291 {
292   list<InputPort *> ret=DynParaLoop::getSetOfInputPort();
293   ret.push_back((InputPort *)&_portForInitFile);
294   ret.push_back((InputPort *)&_retPortForOutPool);
295   return ret;
296 }
297
298 std::list<InputPort *> OptimizerLoop::getLocalInputPorts() const
299 {
300   list<InputPort *> ret=DynParaLoop::getLocalInputPorts();
301   ret.push_back((InputPort *)&_portForInitFile);
302   ret.push_back((InputPort *)&_retPortForOutPool);
303   return ret;
304 }
305
306 void OptimizerLoop::selectRunnableTasks(std::vector<Task *>& tasks)
307 {
308 }
309
310 void OptimizerLoop::getReadyTasks(std::vector<Task *>& tasks)
311 {
312   if(!_node)
313     return;
314   if(_state==YACS::TOACTIVATE || _state==YACS::ACTIVATED)
315     {
316       if(_nodeForSpecialCases)
317         {
318           _nodeForSpecialCases->getReadyTasks(tasks);
319           return ;
320         }
321       for(vector<Node *>::iterator iter=_execNodes.begin();iter!=_execNodes.end();iter++)
322         (*iter)->getReadyTasks(tasks);
323       for(vector<Node *>::iterator iter2=_execInitNodes.begin();iter2!=_execInitNodes.end();iter2++)
324         (*iter2)->getReadyTasks(tasks);
325     }
326     return;
327   
328 }
329
330 YACS::Event OptimizerLoop::updateStateOnFinishedEventFrom(Node *node)
331 {
332   unsigned int id;
333   switch(getIdentityOfNotifyerNode(node,id))
334     {
335     case INIT_NODE:
336       _execNodes[id]->exUpdateState();
337       _nbOfEltConsumed++;
338       break;
339     case WORK_NODE:
340       if(_state==YACS::DONE)//This case happend when alg has reached its convergence whereas other calculations still compute.
341         {
342           if(isFullyLazy())
343             _condForCompletenessB4Relaunch.wait();
344           return YACS::NOEVENT;
345         }
346       _myPool.putOutSampleAt(_execIds[id],_interceptorsForOutPool[id]->getValue());
347       _myPool.setCurrentId(_execIds[id]);
348       _alg->takeDecision();
349       _myPool.destroyCurrentCase();
350       if(_myPool.empty())
351         {
352           pushValueOutOfScopeForCase(id);
353           _execIds[id]=NOT_RUNNING_BRANCH_ID;
354           if(!isFullyLazy())// This case happens when the hand is returned to continue, whereas some other are working in parallel for nothing.
355             _convergenceReachedWithOtherCalc=true;
356           _state=YACS::DONE;
357           return YACS::FINISH;
358         }
359       _execIds[id]=NOT_RUNNING_BRANCH_ID;
360       int newId;
361       unsigned char priority;
362       Any *val=_myPool.getNextSampleWithHighestPriority(newId, priority);
363       if(!val)
364         {
365           bool isFinished=true;
366           for(int i=0;i<_execIds.size() and isFinished;i++)
367             isFinished=(_execIds[i]==NOT_RUNNING_BRANCH_ID);
368           if(isFinished)
369             {
370               std::cerr <<"OptimizerLoop::updateStateOnFinishedEventFrom: Alg has not inserted more cases whereas last element has been calculated !" << std::endl;
371               exForwardFailed();
372               _state=YACS::INTERNALERR;
373               return YACS::FINISH;
374             }
375           return YACS::NOEVENT;
376         }
377       launchMaxOfSamples(false);
378     }
379   return YACS::NOEVENT;
380 }
381
382 void OptimizerLoop::checkNoCyclePassingThrough(Node *node) throw(Exception)
383 {
384 }
385
386 void OptimizerLoop::buildDelegateOf(InPort * & port, OutPort *initialStart, const std::list<ComposedNode *>& pointsOfView)
387 {
388   DynParaLoop::buildDelegateOf(port,initialStart,pointsOfView);
389   if(port==&_retPortForOutPool)
390     throw Exception("OptimizerLoop::buildDelegateOf : uncorrect OptimizerLoop link : out pool port must be linked within the scope of OptimizerLoop node it belongs to.");
391 }
392
393 void OptimizerLoop::buildDelegateOf(std::pair<OutPort *, OutPort *>& port, InPort *finalTarget, const std::list<ComposedNode *>& pointsOfView)
394 {
395   DynParaLoop::buildDelegateOf(port,finalTarget,pointsOfView);
396   string typeOfPortInstance=(port.first)->getNameOfTypeOfCurrentInstance();
397   if(typeOfPortInstance!=OutputPort::NAME)
398     throw Exception("OptimizerLoop::buildDelegateOf : not implemented for DS because not specified ");
399 }
400
401 void OptimizerLoop::checkControlDependancy(OutPort *start, InPort *end, bool cross,
402                                            std::map < ComposedNode *,  std::list < OutPort * >, SortHierarc >& fw,
403                                            std::vector<OutPort *>& fwCross,
404                                            std::map< ComposedNode *, std::list < OutPort *>, SortHierarc >& bw,
405                                            LinkInfo& info) const
406 {
407   if(end==&_retPortForOutPool)
408     fw[(ComposedNode *)this].push_back(start);
409   else
410     DynParaLoop::checkControlDependancy(start,end,cross,fw,fwCross,bw,info);
411 }
412
413 void OptimizerLoop::checkCFLinks(const std::list<OutPort *>& starts, InputPort *end, unsigned char& alreadyFed, bool direction, LinkInfo& info) const
414 {
415   if(end==&_retPortForOutPool)
416     solveObviousOrDelegateCFLinks(starts,end,alreadyFed,direction,info);
417   else
418     DynParaLoop::checkCFLinks(starts,end,alreadyFed,direction,info);
419 }
420
421 void OptimizerLoop::cleanInterceptors()
422 {
423   //the destruction of interceptors whereas some running nodes can push value on them can lead to SIG SEGV.
424   if(!_execNodes.empty())
425     {
426       if(_convergenceReachedWithOtherCalc)
427         {
428           cout << "Waiting completion of last other useless cases." << endl;
429           _condForCompletenessB4Relaunch.waitForAWait();
430         }
431     }
432   // At this point all garanties taken let's clean all.
433   map<InputPort *,vector<InputPort *> >::iterator iter=_interceptors.begin();
434   for(;iter!=_interceptors.end();iter++)
435     for(vector<InputPort *>::iterator iter2=(*iter).second.begin();iter2!=(*iter).second.end();iter2++)
436       delete (*iter2);
437   _interceptors.clear();
438   for(vector<AnyInputPort *>::iterator iter3=_interceptorsForOutPool.begin();iter3!=_interceptorsForOutPool.end();iter3++)
439     delete (*iter3);
440   _interceptorsForOutPool.clear();
441 }
442
443 void OptimizerLoop::launchMaxOfSamples(bool first)
444 {
445   int id;
446   unsigned char priority;
447   Any *val;
448   unsigned i;
449   for(val=_myPool.getNextSampleWithHighestPriority(id,priority);!isFullyBusy(i) && val;val=_myPool.getNextSampleWithHighestPriority(id,priority))
450     {
451       _execIds[i]=id;
452       _myPool.markIdAsInUse(id);
453       if(_initNode)
454         {
455           if(!_initNodeUpdated[i])
456             {
457               putValueOnBranch(val,i,first);
458               _execInitNodes[i]->exUpdateState();
459               _initNodeUpdated[i]=true;
460             }
461           else
462             {
463               putValueOnBranch(val,i,first);
464               _execNodes[i]->exUpdateState();
465               _nbOfEltConsumed++;
466             }
467         }
468       else
469         {
470           if(!first)
471             _execNodes[i]->init(first);
472           putValueOnBranch(val,i,first);
473           _execNodes[i]->exUpdateState();
474           _nbOfEltConsumed++;
475         }
476     }
477 }
478
479 bool OptimizerLoop::isFullyLazy() const
480 {
481   bool isLazy=true;
482   for(unsigned i=0;i<_execIds.size() and isLazy;i++)
483     isLazy=(_execIds[i]==NOT_RUNNING_BRANCH_ID);
484   return isLazy;
485 }
486
487 /*!
488  * Returns if a dynamic branch is available.
489  * \param branchId Out param. Only usable if returned value is equal to \b false.
490  */
491 bool OptimizerLoop::isFullyBusy(unsigned& branchId) const
492 {
493   bool isFinished=true;
494   unsigned i;
495   for(i=0;i<_execIds.size() and isFinished;i++)
496     isFinished=(_execIds[i]!=NOT_RUNNING_BRANCH_ID);
497   if(!isFinished)
498     branchId=i-1;
499   return isFinished;
500 }
501
502 /*!
503  * Perform initialization of interceptors. \b WARNING _execNodes have to be created before.
504  */
505 void OptimizerLoop::initInterceptors(unsigned nbOfBr)
506 {
507   //For all classical outputports leaving 'this'
508   set<OutPort *> portsToIntercept=getAllOutPortsLeavingCurrentScope();
509   for(set<OutPort *>::iterator iter=portsToIntercept.begin();iter!=portsToIntercept.end();iter++)
510     {
511       OutputPort *portC=(OutputPort *)(*iter);//Warrantied by OptimizerLoop::buildDelegateOf
512       const set<InputPort *>& links=portC->getSetOfPhyLinks();
513       for(set<InputPort *>::const_iterator iter2=links.begin();iter2!=links.end();iter2++)
514         {
515 #ifdef NOCOVARIANT
516           InputPort *reprCur=dynamic_cast<InputPort *>((*iter2)->getPublicRepresentant());
517 #else
518           InputPort *reprCur=(*iter2)->getPublicRepresentant();
519 #endif
520           if(!isInMyDescendance(reprCur->getNode()))
521             {//here we've got an out of scope link : Let's intercept it
522               if(_interceptors.find(reprCur)==_interceptors.end())
523                 {
524                   _interceptors[reprCur].resize(nbOfBr);
525                   for(unsigned i=0;i<nbOfBr;i++)
526                     {
527                       OutputPort *portExecC=(OutputPort *)_execNodes[i]->getOutputPort(_node->getOutPortName(portC));
528                       InputPort *clone=reprCur->clone(0);
529                       _interceptors[reprCur][i]=clone;
530                       portExecC->edAddInputPort(clone);
531                     }
532                 }
533               else
534                 {
535                   for(unsigned i=0;i<nbOfBr;i++)
536                     {
537                       OutputPort *portExecC=(OutputPort *)_execNodes[i]->getOutputPort(_node->getOutPortName(portC));
538                       portExecC->edAddInputPort(_interceptors[reprCur][i]);
539                     }
540                 }
541             }
542         }
543     }
544   // For out pool
545   _interceptorsForOutPool.resize(nbOfBr);
546   set< OutPort * > links=_retPortForOutPool.edSetOutPort();
547   for(unsigned i=0;i<nbOfBr;i++)
548     _interceptorsForOutPool[i]=(AnyInputPort *)_retPortForOutPool.clone(this);
549   for(set<OutPort *>::iterator iter2=links.begin();iter2!=links.end();iter2++)
550     for(unsigned j=0;j<nbOfBr;j++)
551       {
552         OutPort *portExec;
553         Node *whatType=isInMyDescendance((*iter2)->getNode());
554         if(whatType==_node)
555           {
556             portExec=_execNodes[j]->getOutPort(_node->getOutPortName(*iter2));
557             portExec->addInPort(_interceptorsForOutPool[j]);
558           }
559         else if(whatType==_initNode && whatType!=0)//This case should never happend. Useless !
560           {
561             portExec=_execInitNodes[j]->getOutPort(_node->getOutPortName(*iter2));
562             portExec->addInPort(_interceptorsForOutPool[j]);
563           }
564       }
565 }
566
567 /*!
568  * Typically called when _alg has decided that convergence has been reached. In this case the links leaving the current scope are activated and filled
569  * with value of the branch specified by 'branchId' that is the branch in which the convergence has been reached.
570  */
571 void OptimizerLoop::pushValueOutOfScopeForCase(unsigned branchId)
572 {
573   map<InputPort *, std::vector<InputPort *> >::iterator iter;
574   for(iter=_interceptors.begin();iter!=_interceptors.end();iter++)
575     (*iter).first->put((*iter).second[branchId]->get());
576 }
577