OpenWalnut 1.2.5

WModule.cpp

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 
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends