1 // SALOME Container : implementation of container and engine for Kernel
3 // Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
24 // File : Container_i.cxx
25 // Author : Paul RASCLE, EDF - MARC TAJCHMAN, CEA
29 //#define private public
30 #include <SALOMEconfig.h>
32 #include CORBA_SERVER_HEADER(SALOME_Component)
34 #include <SALOME_Component.hh>
36 #include <pthread.h> // must be before Python.h !
38 #include "SALOME_Container_i.hxx"
39 #include "SALOME_Component_i.hxx"
40 #include "SALOME_NamingService.hxx"
48 #include "../../adm/win32/SALOME_WNT.hxx"
53 #include "Container_init_python.hxx"
55 #include "utilities.h"
58 bool _Sleeping = false ;
60 // // Needed by multi-threaded Python --- Supervision
65 // Containers with name FactoryServer are started via rsh in LifeCycleCORBA
66 // Other Containers are started via start_impl of FactoryServer
68 extern "C" {void ActSigIntHandler() ; }
70 extern "C" {void SigIntHandler(int, siginfo_t *, void *) ; }
72 extern "C" {void SigIntHandler( int ) ; }
76 map<std::string, int> Engines_Container_i::_cntInstances_map;
77 map<std::string, void *> Engines_Container_i::_library_map;
78 map<std::string, void *> Engines_Container_i::_toRemove_map;
79 omni_mutex Engines_Container_i::_numInstanceMutex ;
81 //=============================================================================
83 * Default constructor, not for use
85 //=============================================================================
87 Engines_Container_i::Engines_Container_i () :
92 //=============================================================================
96 //=============================================================================
98 Engines_Container_i::Engines_Container_i (CORBA::ORB_ptr orb,
99 PortableServer::POA_ptr poa,
100 char *containerName ,
101 int argc , char* argv[],
103 bool isServantAloneInProcess
105 _numInstance(0),_isServantAloneInProcess(isServantAloneInProcess)
107 _pid = (long)getpid();
115 string hostname = GetHostname();
116 MESSAGE(hostname << " " << getpid() << " Engines_Container_i starting argc "
117 << _argc << " Thread " << pthread_self() ) ;
122 MESSAGE(" argv" << i << " " << _argv[ i ]) ;
128 INFOS("SALOME_Container usage : SALOME_Container ServerName");
132 _isSupervContainer = false;
133 if (strcmp(argv[1],"SuperVisionContainer") == 0) _isSupervContainer = true;
135 if (_isSupervContainer)
141 _orb = CORBA::ORB::_duplicate(orb) ;
142 _poa = PortableServer::POA::_duplicate(poa) ;
144 // Pour les containers paralleles: il ne faut pas enregistrer et activer
145 // le container generique, mais le container specialise
149 _id = _poa->activate_object(this);
150 _NS = new SALOME_NamingService();
151 _NS->init_orb( CORBA::ORB::_duplicate(_orb) ) ;
152 CORBA::Object_var obj=_poa->id_to_reference(*_id);
153 Engines::Container_var pCont
154 = Engines::Container::_narrow(obj);
156 _containerName = _NS->BuildContainerNameForNS(containerName,
158 SCRUTE(_containerName);
159 _NS->Register(pCont, _containerName.c_str());
160 MESSAGE("Engines_Container_i::Engines_Container_i : Container name "
164 // import SALOME_Container
165 // pycont = SALOME_Container.SALOME_Container_i(containerIORStr)
167 CORBA::String_var sior = _orb->object_to_string(pCont);
168 string myCommand="pyCont = SALOME_Container.SALOME_Container_i('";
169 myCommand += _containerName + "','";
174 if (!_isSupervContainer)
176 Py_ACQUIRE_NEW_THREAD;
178 // mpv: this is temporary solution: there is a unregular crash if not
180 PyRun_SimpleString("import sys\n");
181 // first element is the path to Registry.dll, but it's wrong
182 PyRun_SimpleString("sys.path = sys.path[1:]\n");
184 PyRun_SimpleString("import SALOME_Container\n");
185 PyRun_SimpleString((char*)myCommand.c_str());
186 Py_RELEASE_NEW_THREAD;
191 //=============================================================================
195 //=============================================================================
197 Engines_Container_i::~Engines_Container_i()
199 MESSAGE("Container_i::~Container_i()");
203 //=============================================================================
205 * CORBA attribute: Container name (see constructor)
207 //=============================================================================
209 char* Engines_Container_i::name()
211 return CORBA::string_dup(_containerName.c_str()) ;
214 //=============================================================================
216 * CORBA method: Get the hostName of the Container (without domain extensions)
218 //=============================================================================
220 char* Engines_Container_i::getHostName()
222 string s = GetHostname();
223 // MESSAGE("Engines_Container_i::getHostName " << s);
224 return CORBA::string_dup(s.c_str()) ;
227 //=============================================================================
229 * CORBA method: Get the PID (process identification) of the Container
231 //=============================================================================
233 CORBA::Long Engines_Container_i::getPID()
235 return (CORBA::Long)getpid();
238 //=============================================================================
240 * CORBA method: check if servant is still alive
242 //=============================================================================
244 void Engines_Container_i::ping()
246 MESSAGE("Engines_Container_i::ping() pid "<< getpid());
249 //=============================================================================
251 * CORBA method, oneway: Server shutdown.
252 * - Container name removed from naming service,
253 * - servant deactivation,
254 * - orb shutdown if no other servants in the process
256 //=============================================================================
258 void Engines_Container_i::Shutdown()
260 MESSAGE("Engines_Container_i::Shutdown()");
261 _NS->Destroy_FullDirectory(_containerName.c_str());
263 //_poa->deactivate_object(*_id);
264 if(_isServantAloneInProcess)
266 MESSAGE("Effective Shutdown of container Begins...");
267 LocalTraceBufferPool* bp1 = LocalTraceBufferPool::instance();
268 bp1->deleteInstance(bp1);
274 //=============================================================================
276 * CORBA method: load a new component class (Python or C++ implementation)
277 * \param componentName like COMPONENT
278 * try to make a Python import of COMPONENT,
279 * then a lib open of libCOMPONENTEngine.so
280 * \return true if dlopen successfull or already done, false otherwise
282 //=============================================================================
285 Engines_Container_i::load_component_Library(const char* componentName)
288 string aCompName = componentName;
290 // --- try dlopen C++ component
293 string impl_name = string ("lib") + aCompName + string("Engine.so");
295 string impl_name = aCompName + string("Engine.dll");
299 _numInstanceMutex.lock(); // lock to be alone
300 // (see decInstanceCnt, finalize_removal))
301 if (_toRemove_map[impl_name]) _toRemove_map.erase(impl_name);
302 if (_library_map[impl_name])
304 MESSAGE("Library " << impl_name << " already loaded");
305 _numInstanceMutex.unlock();
311 handle = dlopen( impl_name.c_str() , RTLD_LAZY ) ;
313 handle = dlopen( impl_name.c_str() , 0 ) ;
317 _library_map[impl_name] = handle;
318 _numInstanceMutex.unlock();
323 INFOS("Can't load shared library : " << impl_name);
324 INFOS("error dlopen: " << dlerror());
326 _numInstanceMutex.unlock();
328 // --- try import Python component
330 INFOS("try import Python component "<<componentName);
331 if (_isSupervContainer)
333 INFOS("Supervision Container does not support Python Component Engines");
336 if (_library_map[aCompName])
338 return true; // Python Component, already imported
342 Py_ACQUIRE_NEW_THREAD;
343 PyObject *mainmod = PyImport_AddModule("__main__");
344 PyObject *globals = PyModule_GetDict(mainmod);
345 PyObject *pyCont = PyDict_GetItemString(globals, "pyCont");
346 PyObject *result = PyObject_CallMethod(pyCont,
349 int ret= PyInt_AsLong(result);
351 Py_RELEASE_NEW_THREAD;
353 if (ret) // import possible: Python component
355 _library_map[aCompName] = (void *)pyCont; // any non O value OK
356 MESSAGE("import Python: "<<aCompName<<" OK");
363 //=============================================================================
365 * CORBA method: Creates a new servant instance of a component.
366 * The servant registers itself to naming service and Registry.
367 * \param genericRegisterName Name of the component instance to register
368 * in Registry & Name Service (without _inst_n suffix)
369 * \param studyId 0 for multiStudy instance,
370 * study Id (>0) otherwise
371 * \return a loaded component
373 //=============================================================================
375 Engines::Component_ptr
376 Engines_Container_i::create_component_instance(const char*genericRegisterName,
381 INFOS("studyId must be > 0 for mono study instance, =0 for multiStudy");
382 return Engines::Component::_nil() ;
385 Engines::Component_var iobject = Engines::Component::_nil() ;
387 string aCompName = genericRegisterName;
388 if (_library_map[aCompName]) // Python component
390 if (_isSupervContainer)
392 INFOS("Supervision Container does not support Python Component Engines");
393 return Engines::Component::_nil();
395 _numInstanceMutex.lock() ; // lock on the instance number
397 int numInstance = _numInstance ;
398 _numInstanceMutex.unlock() ;
401 sprintf( aNumI , "%d" , numInstance ) ;
402 string instanceName = aCompName + "_inst_" + aNumI ;
403 string component_registerName =
404 _containerName + "/" + instanceName;
406 Py_ACQUIRE_NEW_THREAD;
407 PyObject *mainmod = PyImport_AddModule("__main__");
408 PyObject *globals = PyModule_GetDict(mainmod);
409 PyObject *pyCont = PyDict_GetItemString(globals, "pyCont");
410 PyObject *result = PyObject_CallMethod(pyCont,
411 "create_component_instance",
414 instanceName.c_str(),
416 string iors = PyString_AsString(result);
418 Py_RELEASE_NEW_THREAD;
420 CORBA::Object_var obj = _orb->string_to_object(iors.c_str());
421 iobject = Engines::Component::_narrow( obj ) ;
422 return iobject._retn();
428 string impl_name = string ("lib") + genericRegisterName +string("Engine.so");
430 string impl_name = genericRegisterName +string("Engine.dll");
432 void* handle = _library_map[impl_name];
435 INFOS("shared library " << impl_name <<"must be loaded before instance");
436 return Engines::Component::_nil() ;
440 iobject = createInstance(genericRegisterName,
443 return iobject._retn();
447 //=============================================================================
449 * CORBA method: Finds a servant instance of a component
450 * \param registeredName Name of the component in Registry or Name Service,
451 * without instance suffix number
452 * \param studyId 0 if instance is not associated to a study,
453 * >0 otherwise (== study id)
454 * \return the first instance found with same studyId
456 //=============================================================================
458 Engines::Component_ptr
459 Engines_Container_i::find_component_instance( const char* registeredName,
462 Engines::Component_var anEngine = Engines::Component::_nil();
463 map<string,Engines::Component_var>::iterator itm =_listInstances_map.begin();
464 while (itm != _listInstances_map.end())
466 string instance = (*itm).first;
468 if (instance.find(registeredName) == 0)
470 anEngine = (*itm).second;
471 if (studyId == anEngine->getStudyId())
473 return anEngine._retn();
478 return anEngine._retn();
481 //=============================================================================
483 * CORBA method: find or create an instance of the component (servant),
484 * load a new component class (dynamic library) if required,
485 * ---- FOR COMPATIBILITY WITH 2.2 ----
486 * ---- USE ONLY FOR MULTISTUDY INSTANCES ! --------
487 * The servant registers itself to naming service and Registry.
488 * \param genericRegisterName Name of the component to register
489 * in Registry & Name Service
490 * \param componentName Name of the constructed library of the component
491 * \return a loaded component
493 //=============================================================================
495 Engines::Component_ptr
496 Engines_Container_i::load_impl( const char* genericRegisterName,
497 const char* componentName )
499 string impl_name = string ("lib") + genericRegisterName +string("Engine.so");
500 Engines::Component_var iobject = Engines::Component::_nil() ;
501 if (load_component_Library(genericRegisterName))
502 iobject = find_or_create_instance(genericRegisterName, impl_name);
503 return iobject._retn();
507 //=============================================================================
509 * CORBA method: Stops the component servant, and deletes all related objects
510 * \param component_i Component to be removed
512 //=============================================================================
514 void Engines_Container_i::remove_impl(Engines::Component_ptr component_i)
516 ASSERT(! CORBA::is_nil(component_i));
517 string instanceName = component_i->instanceName() ;
518 MESSAGE("unload component " << instanceName);
519 _listInstances_map.erase(instanceName);
520 component_i->destroy() ;
521 _NS->Destroy_Name(instanceName.c_str());
524 //=============================================================================
526 * CORBA method: Discharges unused libraries from the container.
528 //=============================================================================
530 void Engines_Container_i::finalize_removal()
532 MESSAGE("finalize unload : dlclose");
533 _numInstanceMutex.lock(); // lock to be alone
534 // (see decInstanceCnt, load_component_Library)
535 map<string, void *>::iterator ith;
536 for (ith = _toRemove_map.begin(); ith != _toRemove_map.end(); ith++)
538 void *handle = (*ith).second;
539 string impl_name= (*ith).first;
544 // dlclose(handle); // SALOME unstable after ...
545 // _library_map.erase(impl_name);
548 _toRemove_map.clear();
549 _numInstanceMutex.unlock();
552 //=============================================================================
554 * CORBA method: Kill the container process with exit(0).
555 * To remove : never returns !
557 //=============================================================================
559 bool Engines_Container_i::Kill_impl()
561 MESSAGE("Engines_Container_i::Kill() pid "<< getpid() << " containerName "
562 << _containerName.c_str() << " machineName "
563 << GetHostname().c_str());
564 INFOS("===============================================================");
565 INFOS("= REMOVE calls to Kill_impl in C++ container =");
566 INFOS("===============================================================");
572 //=============================================================================
574 * C++ method: Finds an already existing servant instance of a component, or
575 * create an instance.
576 * ---- USE ONLY FOR MULTISTUDY INSTANCES ! --------
577 * \param genericRegisterName Name of the component instance to register
578 * in Registry & Name Service,
579 * (without _inst_n suffix, like "COMPONENT")
580 * \param componentLibraryName like "libCOMPONENTEngine.so"
581 * \return a loaded component
583 * example with names:
584 * aGenRegisterName = COMPONENT (= first argument)
585 * impl_name = libCOMPONENTEngine.so (= second argument)
586 * _containerName = /Containers/cli76ce/FactoryServer
587 * factoryName = COMPONENTEngine_factory
588 * component_registerBase = /Containers/cli76ce/FactoryServer/COMPONENT
590 * instanceName = COMPONENT_inst_1
591 * component_registerName = /Containers/cli76ce/FactoryServer/COMPONENT_inst_1
593 //=============================================================================
595 Engines::Component_ptr
596 Engines_Container_i::find_or_create_instance(string genericRegisterName,
597 string componentLibraryName)
599 string aGenRegisterName = genericRegisterName;
600 string impl_name = componentLibraryName;
601 void* handle = _library_map[impl_name];
604 INFOS("shared library " << impl_name <<"must be loaded before instance");
605 return Engines::Component::_nil() ;
609 // --- find a registered instance in naming service, or create
611 string component_registerBase =
612 _containerName + "/" + aGenRegisterName;
613 Engines::Component_var iobject = Engines::Component::_nil() ;
616 CORBA::Object_var obj =
617 _NS->ResolveFirst( component_registerBase.c_str());
618 if ( CORBA::is_nil( obj ) )
620 iobject = createInstance(genericRegisterName,
622 0); // force multiStudy instance here !
626 iobject = Engines::Component::_narrow( obj ) ;
627 Engines_Component_i *servant =
628 dynamic_cast<Engines_Component_i*>
629 (_poa->reference_to_servant(iobject));
631 int studyId = servant->getStudyId();
632 ASSERT (studyId >= 0);
633 if (studyId == 0) // multiStudy instance, OK
636 MESSAGE(component_registerBase.c_str()<<" already bound");
638 else // monoStudy instance: NOK
640 iobject = Engines::Component::_nil();
641 INFOS("load_impl & find_component_instance methods "
642 << "NOT SUITABLE for mono study components");
648 INFOS( "Container_i::load_impl catched" ) ;
650 return iobject._retn();
654 //=============================================================================
656 * C++ method: create a servant instance of a component.
657 * \param genericRegisterName Name of the component instance to register
658 * in Registry & Name Service,
659 * (without _inst_n suffix, like "COMPONENT")
660 * \param handle loaded library handle
661 * \param studyId 0 for multiStudy instance,
662 * study Id (>0) otherwise
663 * \return a loaded component
665 * example with names:
666 * aGenRegisterName = COMPONENT (= first argument)
667 * _containerName = /Containers/cli76ce/FactoryServer
668 * factoryName = COMPONENTEngine_factory
669 * component_registerBase = /Containers/cli76ce/FactoryServer/COMPONENT
670 * instanceName = COMPONENT_inst_1
671 * component_registerName = /Containers/cli76ce/FactoryServer/COMPONENT_inst_1
673 //=============================================================================
675 Engines::Component_ptr
676 Engines_Container_i::createInstance(string genericRegisterName,
680 // --- find the factory
682 string aGenRegisterName = genericRegisterName;
683 string factory_name = aGenRegisterName + string("Engine_factory");
684 SCRUTE(factory_name) ;
686 typedef PortableServer::ObjectId * (*FACTORY_FUNCTION)
688 PortableServer::POA_ptr,
689 PortableServer::ObjectId *,
693 FACTORY_FUNCTION Component_factory
694 = (FACTORY_FUNCTION) dlsym(handle, factory_name.c_str());
697 if ( (error = dlerror() ) != NULL)
699 INFOS("Can't resolve symbol: " + factory_name);
701 return Engines::Component::_nil() ;
704 // --- create instance
706 Engines::Component_var iobject = Engines::Component::_nil() ;
710 _numInstanceMutex.lock() ; // lock on the instance number
712 int numInstance = _numInstance ;
713 _numInstanceMutex.unlock() ;
716 sprintf( aNumI , "%d" , numInstance ) ;
717 string instanceName = aGenRegisterName + "_inst_" + aNumI ;
718 string component_registerName =
719 _containerName + "/" + instanceName;
721 // --- Instanciate required CORBA object
723 PortableServer::ObjectId *id ; //not owner, do not delete (nore use var)
724 id = (Component_factory) ( _orb, _poa, _id, instanceName.c_str(),
725 aGenRegisterName.c_str() ) ;
727 // --- get reference & servant from id
729 CORBA::Object_var obj = _poa->id_to_reference(*id);
730 iobject = Engines::Component::_narrow( obj ) ;
732 Engines_Component_i *servant =
733 dynamic_cast<Engines_Component_i*>(_poa->reference_to_servant(iobject));
735 //SCRUTE(servant->pd_refCount);
736 servant->_remove_ref(); // compensate previous id_to_reference
737 //SCRUTE(servant->pd_refCount);
738 _listInstances_map[instanceName] = iobject;
739 _cntInstances_map[aGenRegisterName] += 1;
740 SCRUTE(aGenRegisterName);
741 SCRUTE(_cntInstances_map[aGenRegisterName]);
742 //SCRUTE(servant->pd_refCount);
743 bool ret_studyId = servant->setStudyId(studyId);
746 // --- register the engine under the name
747 // containerName(.dir)/instanceName(.object)
749 _NS->Register( iobject , component_registerName.c_str() ) ;
750 MESSAGE( component_registerName.c_str() << " bound" ) ;
754 INFOS( "Container_i::createInstance exception catched" ) ;
756 return iobject._retn();
759 //=============================================================================
763 //=============================================================================
765 void Engines_Container_i::decInstanceCnt(string genericRegisterName)
767 string aGenRegisterName =genericRegisterName;
768 MESSAGE("Engines_Container_i::decInstanceCnt " << aGenRegisterName);
769 ASSERT(_cntInstances_map[aGenRegisterName] > 0);
770 _numInstanceMutex.lock(); // lock to be alone
771 // (see finalize_removal, load_component_Library)
772 _cntInstances_map[aGenRegisterName] -= 1;
773 SCRUTE(_cntInstances_map[aGenRegisterName]);
774 if (_cntInstances_map[aGenRegisterName] == 0)
777 Engines_Component_i::GetDynLibraryName(aGenRegisterName.c_str());
779 void* handle = _library_map[impl_name];
781 _toRemove_map[impl_name] = handle;
783 _numInstanceMutex.unlock();
786 //=============================================================================
788 * Retrieves only with container naming convention if it is a python container
790 //=============================================================================
792 bool Engines_Container_i::isPythonContainer(const char* ContainerName)
795 int len=strlen(ContainerName);
797 if(strcmp(ContainerName+len-2,"Py")==0)
802 //=============================================================================
806 //=============================================================================
808 void ActSigIntHandler()
811 struct sigaction SigIntAct ;
812 SigIntAct.sa_sigaction = &SigIntHandler ;
813 SigIntAct.sa_flags = SA_SIGINFO ;
816 // DEBUG 03.02.2005 : the first parameter of sigaction is not a mask of signals
817 // (SIGINT | SIGUSR1) :
818 // it must be only one signal ===> one call for SIGINT
819 // and an other one for SIGUSR1
822 if ( sigaction( SIGINT , &SigIntAct, NULL ) )
824 perror("SALOME_Container main ") ;
827 if ( sigaction( SIGUSR1 , &SigIntAct, NULL ) )
829 perror("SALOME_Container main ") ;
833 //PAL9042 JR : during the execution of a Signal Handler (and of methods called through Signal Handlers)
834 // use of streams (and so on) should never be used because :
835 // streams of C++ are naturally thread-safe and use pthread_mutex_lock ===>
836 // A stream operation may be interrupted by a signal and if the Handler use stream we
837 // may have a "Dead-Lock" ===HangUp
838 //==INFOS is commented
839 // INFOS(pthread_self() << "SigIntHandler activated") ;
842 signal( SIGINT, SigIntHandler );
843 signal( SIGUSR1, SigIntHandler );
851 void SigIntHandler(int what ,
852 siginfo_t * siginfo ,
855 //PAL9042 JR : during the execution of a Signal Handler (and of methods called through Signal Handlers)
856 // use of streams (and so on) should never be used because :
857 // streams of C++ are naturally thread-safe and use pthread_mutex_lock ===>
858 // A stream operation may be interrupted by a signal and if the Handler use stream we
859 // may have a "Dead-Lock" ===HangUp
860 //==MESSAGE is commented
861 // MESSAGE(pthread_self() << "SigIntHandler what " << what << endl
862 // << " si_signo " << siginfo->si_signo << endl
863 // << " si_code " << siginfo->si_code << endl
864 // << " si_pid " << siginfo->si_pid) ;
869 // MESSAGE("SigIntHandler END sleeping.") ;
875 if ( siginfo->si_signo == SIGUSR1 )
882 // MESSAGE("SigIntHandler BEGIN sleeping.") ;
889 // MESSAGE("SigIntHandler LEAVE sleeping after " << count << " s.") ;
895 void SigIntHandler( int what )
897 MESSAGE( pthread_self() << "SigIntHandler what " << what << endl );
901 MESSAGE("SigIntHandler END sleeping.") ;
907 if ( what == SIGUSR1 )
914 MESSAGE("SigIntHandler BEGIN sleeping.") ;
921 MESSAGE("SigIntHandler LEAVE sleeping after " << count << " s.") ;