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