OpenWalnut 1.2.5
|
00001 //--------------------------------------------------------------------------- 00002 // 00003 // Project: OpenWalnut ( http://www.openwalnut.org ) 00004 // 00005 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS 00006 // For more information see http://www.openwalnut.org/copying 00007 // 00008 // This file is part of OpenWalnut. 00009 // 00010 // OpenWalnut is free software: you can redistribute it and/or modify 00011 // it under the terms of the GNU Lesser General Public License as published by 00012 // the Free Software Foundation, either version 3 of the License, or 00013 // (at your option) any later version. 00014 // 00015 // OpenWalnut is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 // GNU Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public License 00021 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>. 00022 // 00023 //--------------------------------------------------------------------------- 00024 00025 #ifdef __linux__ 00026 #include <sys/prctl.h> 00027 #endif 00028 00029 #include <algorithm> 00030 #include <set> 00031 #include <string> 00032 #include <sstream> 00033 00034 #include <boost/shared_ptr.hpp> 00035 00036 #include "WModuleInputConnector.h" 00037 #include "WModuleOutputConnector.h" 00038 #include "WModuleInputData.h" 00039 #include "WModuleOutputData.h" 00040 #include "WModuleConnectorSignals.h" 00041 #include "WModuleContainer.h" 00042 #include "WModuleFactory.h" 00043 #include "exceptions/WModuleSignalUnknown.h" 00044 #include "exceptions/WModuleSignalSubscriptionFailed.h" 00045 #include "exceptions/WModuleConnectorInitFailed.h" 00046 #include "exceptions/WModuleConnectorNotFound.h" 00047 #include "exceptions/WModuleUninitialized.h" 00048 #include "exceptions/WModuleRequirementNotMet.h" 00049 #include "../common/WException.h" 00050 #include "../common/exceptions/WNameNotUnique.h" 00051 #include "../common/WLogger.h" 00052 #include "../common/WCondition.h" 00053 #include "../common/WConditionOneShot.h" 00054 #include "../common/WConditionSet.h" 00055 #include "../common/WPathHelper.h" 00056 #include "../common/WProgressCombiner.h" 00057 #include "../common/WPredicateHelper.h" 00058 00059 #include "WModule.h" 00060 00061 WModule::WModule(): 00062 WThreadedRunner(), 00063 WPrototyped(), 00064 m_initialized( new WCondition(), false ), 00065 m_isAssociated( new WCondition(), false ), 00066 m_isUsable( new WCondition(), false ), 00067 m_isReady( new WConditionOneShot(), false ), 00068 m_isCrashed( new WConditionOneShot(), false ), 00069 m_isReadyOrCrashed( new WConditionSet(), false ), 00070 m_isRunning( new WCondition(), false ), 00071 m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ), 00072 m_moduleState(), 00073 m_localPath( WPathHelper::getSharePath() ) 00074 { 00075 // initialize members 00076 m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) ); 00077 m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) ); 00078 m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION ); 00079 00080 m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but " 00081 "can be changed by the user to provide some kind of simple identification upon many modules.", 00082 std::string( "" ), false ); 00083 00084 m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true ); 00085 m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) ); 00086 00087 // the isReadyOrCrashed condition set needs to be set up here 00088 WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT 00089 cs->setResetable( true, false ); 00090 cs->add( m_isReady.getCondition() ); 00091 cs->add( m_isCrashed.getCondition() ); 00092 00093 m_container = boost::shared_ptr< WModuleContainer >(); 00094 m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() ); 00095 00096 // add a progress indicator which finishes on "ready()" 00097 m_progress->addSubProgress( m_readyProgress ); 00098 00099 // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner. 00100 m_moduleState.add( m_shutdownFlag.getCondition() ); 00101 } 00102 00103 WModule::~WModule() 00104 { 00105 // cleanup 00106 } 00107 00108 void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con ) 00109 { 00110 size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(), 00111 WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() ) 00112 ); 00113 // well ... we want it to be unique in both: 00114 c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(), 00115 WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() ) 00116 ); 00117 00118 // if there already is one ... exception 00119 if( c ) 00120 { 00121 throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) ); 00122 } 00123 00124 m_inputConnectors.push_back( con ); 00125 } 00126 00127 void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con ) 00128 { 00129 size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(), 00130 WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() ) 00131 ); 00132 // well ... we want it to be unique in both: 00133 c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(), 00134 WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() ) 00135 ); 00136 00137 // if there already is one ... exception 00138 if( c ) 00139 { 00140 throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) ); 00141 } 00142 00143 m_outputConnectors.push_back( con ); 00144 } 00145 00146 void WModule::disconnect() 00147 { 00148 // remove connections and their signals 00149 for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); 00150 listIter != m_inputConnectors.end(); ++listIter ) 00151 { 00152 ( *listIter )->disconnectAll(); 00153 } 00154 for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); 00155 listIter != m_outputConnectors.end(); ++listIter ) 00156 { 00157 ( *listIter )->disconnectAll(); 00158 } 00159 } 00160 00161 WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections() 00162 { 00163 WCombinerTypes::WDisconnectList discons; 00164 00165 // iterate inputs 00166 for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter ) 00167 { 00168 // get all connections of the current connector: 00169 WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(), 00170 ( *listIter )->getPossibleDisconnections() ); 00171 00172 if( g.second.size() ) 00173 { 00174 discons.push_back( g ); 00175 } 00176 } 00177 00178 // iterate outputs 00179 for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter ) 00180 { 00181 // get all connections of the current connector: 00182 WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(), 00183 ( *listIter )->getPossibleDisconnections() ); 00184 00185 if( g.second.size() ) 00186 { 00187 discons.push_back( g ); 00188 } 00189 } 00190 00191 return discons; 00192 } 00193 00194 void WModule::removeConnectors() 00195 { 00196 m_initialized( false ); 00197 m_isUsable( m_initialized() && m_isAssociated() ); 00198 00199 // remove connections and their signals, this is flat removal. The module container can do deep removal 00200 disconnect(); 00201 00202 // clean up list 00203 // this should delete the connector since nobody else *should* have another shared_ptr to them 00204 m_inputConnectors.clear(); 00205 m_outputConnectors.clear(); 00206 } 00207 00208 void WModule::connectors() 00209 { 00210 } 00211 00212 void WModule::properties() 00213 { 00214 } 00215 00216 void WModule::requirements() 00217 { 00218 } 00219 00220 void WModule::activate() 00221 { 00222 } 00223 00224 std::string WModule::deprecated() const 00225 { 00226 return ""; 00227 } 00228 00229 void WModule::initialize() 00230 { 00231 // doing it twice is not allowed 00232 if( isInitialized()() ) 00233 { 00234 throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() + 00235 std::string( ". Reason: already initialized." ) ); 00236 } 00237 00238 // set the module name as default runtime name 00239 m_runtimeName->set( getName() ); 00240 00241 // initialize connectors and properties 00242 requirements(); 00243 connectors(); 00244 properties(); 00245 00246 // now, the module is initialized but not necessarily usable (if not associated with a container) 00247 m_initialized( true ); 00248 m_isUsable( m_initialized() && m_isAssociated() ); 00249 } 00250 00251 void WModule::cleanup() 00252 { 00253 // currently just removes connectors 00254 removeConnectors(); 00255 } 00256 00257 boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const 00258 { 00259 return m_container; 00260 } 00261 00262 void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container ) 00263 { 00264 m_container = container; 00265 00266 // true if the pointer is set 00267 m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() ); 00268 m_isUsable( m_initialized() && m_isAssociated() ); 00269 } 00270 00271 MODULE_TYPE WModule::getType() const 00272 { 00273 return MODULE_ARBITRARY; 00274 } 00275 00276 const WModule::InputConnectorList& WModule::getInputConnectors() const 00277 { 00278 return m_inputConnectors; 00279 } 00280 00281 const WModule::OutputConnectorList& WModule::getOutputConnectors() const 00282 { 00283 return m_outputConnectors; 00284 } 00285 00286 boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name ) 00287 { 00288 // simply search 00289 for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin(); 00290 listIter != m_inputConnectors.end(); ++listIter ) 00291 { 00292 // try the canonical name 00293 if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) ) 00294 { 00295 return ( *listIter ); 00296 } 00297 } 00298 00299 return boost::shared_ptr< WModuleInputConnector >(); 00300 } 00301 00302 boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name ) 00303 { 00304 boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name ); 00305 00306 if( !p ) 00307 { 00308 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00309 std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) ); 00310 } 00311 00312 return p; 00313 } 00314 00315 boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name ) 00316 { 00317 // simply search 00318 for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin(); 00319 listIter != m_outputConnectors.end(); ++listIter ) 00320 { 00321 // try the canonical name 00322 if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) ) 00323 { 00324 return ( *listIter ); 00325 } 00326 } 00327 00328 return boost::shared_ptr< WModuleOutputConnector >(); 00329 } 00330 00331 boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name ) 00332 { 00333 boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name ); 00334 00335 if( !p ) 00336 { 00337 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00338 std::string( "\" does not exist in the module \"" ) + getName() + 00339 std::string( "\"." ) ); 00340 } 00341 00342 return p; 00343 } 00344 00345 boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name ) 00346 { 00347 // simply search both 00348 boost::shared_ptr< WModuleConnector > p = findInputConnector( name ); 00349 if( p ) // found? 00350 { 00351 return p; 00352 } 00353 00354 // search in output list 00355 return findOutputConnector( name ); 00356 } 00357 00358 boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name ) 00359 { 00360 boost::shared_ptr< WModuleConnector > p = findConnector( name ); 00361 00362 if( !p ) 00363 { 00364 throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name + 00365 std::string( "\" does not exist in the module \"" ) + getName() + 00366 std::string( "\"." ) ); 00367 } 00368 00369 return p; 00370 } 00371 00372 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier ) 00373 { 00374 switch ( signal ) 00375 { 00376 case WM_READY: 00377 return signal_ready.connect( notifier ); 00378 default: 00379 std::ostringstream s; 00380 s << "Could not subscribe to unknown signal."; 00381 throw WModuleSignalSubscriptionFailed( s.str() ); 00382 break; 00383 } 00384 } 00385 00386 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier ) 00387 { 00388 switch (signal) 00389 { 00390 case WM_ERROR: 00391 return signal_error.connect( notifier ); 00392 default: 00393 std::ostringstream s; 00394 s << "Could not subscribe to unknown signal."; 00395 throw WModuleSignalSubscriptionFailed( s.str() ); 00396 break; 00397 } 00398 } 00399 00400 const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal ) 00401 { 00402 switch ( signal ) 00403 { 00404 case CONNECTION_ESTABLISHED: 00405 return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 ); 00406 case CONNECTION_CLOSED: 00407 return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 ); 00408 case DATA_CHANGED: 00409 return boost::bind( &WModule::notifyDataChange, this, _1, _2 ); 00410 default: 00411 std::ostringstream s; 00412 s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module."; 00413 throw WModuleSignalUnknown( s.str() ); 00414 break; 00415 } 00416 } 00417 00418 const WBoolFlag& WModule::isInitialized() const 00419 { 00420 return m_initialized; 00421 } 00422 00423 const WBoolFlag& WModule::isAssociated() const 00424 { 00425 return m_isAssociated; 00426 } 00427 00428 const WBoolFlag& WModule::isUseable() const 00429 { 00430 return m_isUsable; 00431 //return isInitialized() && isAssociated(); 00432 } 00433 00434 const WBoolFlag& WModule::isReady() const 00435 { 00436 return m_isReady; 00437 } 00438 00439 const WBoolFlag& WModule::isCrashed() const 00440 { 00441 return m_isCrashed; 00442 } 00443 00444 const WBoolFlag& WModule::isReadyOrCrashed() const 00445 { 00446 return m_isReadyOrCrashed; 00447 } 00448 00449 const WBoolFlag& WModule::isRunning() const 00450 { 00451 return m_isRunning; 00452 } 00453 00454 void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/, 00455 boost::shared_ptr< WModuleConnector > /*there*/ ) 00456 { 00457 // By default this callback does nothing. Overwrite it in your module. 00458 } 00459 00460 void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/, 00461 boost::shared_ptr< WModuleConnector > /*there*/ ) 00462 { 00463 // By default this callback does nothing. Overwrite it in your module. 00464 } 00465 00466 void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/, 00467 boost::shared_ptr< WModuleConnector > /*output*/ ) 00468 { 00469 // By default this callback does nothing. Overwrite it in your module. 00470 } 00471 00472 boost::shared_ptr< WProperties > WModule::getProperties() const 00473 { 00474 return m_properties; 00475 } 00476 00477 boost::shared_ptr< WProperties > WModule::getInformationProperties() const 00478 { 00479 return m_infoProperties; 00480 } 00481 00482 boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner() 00483 { 00484 return m_progress; 00485 } 00486 00487 const char** WModule::getXPMIcon() const 00488 { 00489 // return empty 1x1 icon by default. 00490 static const char * o_xpm[] = 00491 { 00492 "1 1 1 1", 00493 " c None", 00494 " " 00495 }; 00496 return o_xpm; 00497 } 00498 00499 void WModule::ready() 00500 { 00501 m_isReady( true ); 00502 m_readyProgress->finish(); 00503 signal_ready( shared_from_this() ); 00504 } 00505 00506 const WRequirement* WModule::checkRequirements() const 00507 { 00508 // simply iterate all requirements and return the first found that is not fulfilled 00509 for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i ) 00510 { 00511 if( !( *i )->isComplied() ) 00512 { 00513 return *i; 00514 } 00515 } 00516 00517 return NULL; 00518 } 00519 00520 void WModule::threadMain() 00521 { 00522 #ifdef __linux__ 00523 // set the name of the thread. This name is shown by the "top", for example. 00524 prctl( PR_SET_NAME, ( "openwalnut (" + getName() + ")" ).c_str() ); 00525 #endif 00526 00527 try 00528 { 00529 WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO ); 00530 00531 // check requirements 00532 const WRequirement* failedReq = checkRequirements(); 00533 if( failedReq ) 00534 { 00535 throw WModuleRequirementNotMet( failedReq ); 00536 } 00537 00538 // call main thread function 00539 m_isRunning( true ); 00540 moduleMain(); 00541 } 00542 catch( const WException& e ) 00543 { 00544 wlog::error( "Module (" + getName() +")" ) << "WException. Notifying. Message: " << e.what(); 00545 00546 // ensure proper exception propagation 00547 signal_error( shared_from_this(), e ); 00548 00549 // hopefully, all waiting threads use isReadyOrCrashed to wait. 00550 m_isCrashed( true ); 00551 } 00552 catch( const std::exception& e ) 00553 { 00554 // convert these exceptions to WException 00555 WException ce = WException( e ); 00556 00557 // print this message AFTER creation of WException to have the backtrace before the message 00558 WLogger::getLogger()->addLogMessage( std::string( "Exception. Notifying. Message: " ) + e.what(), "Module (" + getName() + ")", LL_ERROR ); 00559 00560 // communicate error 00561 signal_error( shared_from_this(), ce ); 00562 00563 // hopefully, all waiting threads use isReadyOrCrashed to wait. 00564 m_isCrashed( true ); 00565 } 00566 00567 // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get 00568 // disconnected in the connector destructor. 00569 disconnect(); 00570 m_isRunning( false ); 00571 } 00572 00573 wlog::WStreamedLogger WModule::infoLog() const 00574 { 00575 return wlog::info( getName() ); 00576 } 00577 00578 wlog::WStreamedLogger WModule::errorLog() const 00579 { 00580 return wlog::error( getName() ); 00581 } 00582 00583 wlog::WStreamedLogger WModule::debugLog() const 00584 { 00585 return wlog::debug( getName() ); 00586 } 00587 00588 wlog::WStreamedLogger WModule::warnLog() const 00589 { 00590 return wlog::warn( getName() ); 00591 } 00592 00593 void WModule::setLocalPath( boost::filesystem::path path ) 00594 { 00595 m_localPath = path; 00596 } 00597 00598 boost::filesystem::path WModule::getLocalPath() const 00599 { 00600 return m_localPath; 00601 } 00602 00603 bool WModule::isDeprecated() const 00604 { 00605 return !deprecated().empty(); 00606 } 00607 00608 std::string WModule::getDeprecationMessage() const 00609 { 00610 return deprecated(); 00611 } 00612