WModule.cpp 18.1 KB
Newer Older
ebaum's avatar
ebaum committed
1 2
//---------------------------------------------------------------------------
//
3
// Project: OpenWalnut ( http://www.openwalnut.org )
ebaum's avatar
ebaum committed
4
//
5 6
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
ebaum's avatar
ebaum committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------

25 26 27
#ifdef __linux__
#include <sys/prctl.h>
#endif
ebaum's avatar
ebaum committed
28

29
#include <algorithm>
Alexander Wiebel's avatar
Alexander Wiebel committed
30 31 32 33
#include <set>
#include <string>
#include <sstream>

34 35 36 37
#include <boost/shared_ptr.hpp>

#include "WModuleInputConnector.h"
#include "WModuleOutputConnector.h"
38 39
#include "WModuleInputData.h"
#include "WModuleOutputData.h"
40
#include "WModuleConnectorSignals.h"
41
#include "WModuleContainer.h"
42
#include "WModuleFactory.h"
43
#include "exceptions/WModuleSignalUnknown.h"
44
#include "exceptions/WModuleSignalSubscriptionFailed.h"
45
#include "exceptions/WModuleConnectorInitFailed.h"
46
#include "exceptions/WModuleConnectorNotFound.h"
47
#include "exceptions/WModuleUninitialized.h"
48
#include "exceptions/WModuleRequirementNotMet.h"
49
#include "../common/WException.h"
50
#include "../common/exceptions/WNameNotUnique.h"
51
#include "../common/WLogger.h"
52 53
#include "../common/WCondition.h"
#include "../common/WConditionOneShot.h"
54
#include "../common/WConditionSet.h"
55
#include "../common/WPathHelper.h"
56
#include "../common/WProgressCombiner.h"
57
#include "../common/WPredicateHelper.h"
58

ebaum's avatar
ebaum committed
59 60
#include "WModule.h"

ebaum's avatar
ebaum committed
61
WModule::WModule():
62
    WThreadedRunner(),
63
    WPrototyped(),
64 65
    m_initialized( new WCondition(), false ),
    m_isAssociated( new WCondition(), false ),
66 67
    m_isUsable( new WCondition(), false ),
    m_isReady( new WConditionOneShot(), false ),
68 69
    m_isCrashed( new WConditionOneShot(), false ),
    m_isReadyOrCrashed( new WConditionSet(), false ),
70
    m_isRunning( new WCondition(), false ),
71
    m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ),
72 73
    m_moduleState(),
    m_localPath( WPathHelper::getSharePath() )
ebaum's avatar
ebaum committed
74 75
{
    // initialize members
76
    m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) );
77
    m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) );
78
    m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION );
79

80 81 82 83
    m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but "
                                                       "can be changed by the user to provide some kind of simple identification upon many modules.",
                                                       std::string( "" ), false );

84
    m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true );
85 86
    m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) );

87 88 89 90 91 92
    // the isReadyOrCrashed condition set needs to be set up here
    WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT
    cs->setResetable( true, false );
    cs->add( m_isReady.getCondition() );
    cs->add( m_isCrashed.getCondition() );

93
    m_container = boost::shared_ptr< WModuleContainer >();
94
    m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() );
95

96 97 98
    // add a progress indicator which finishes on "ready()"
    m_progress->addSubProgress( m_readyProgress );

99 100
    // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner.
    m_moduleState.add( m_shutdownFlag.getCondition() );
ebaum's avatar
ebaum committed
101 102 103 104 105 106 107
}

WModule::~WModule()
{
    // cleanup
}

Alexander Wiebel's avatar
Alexander Wiebel committed
108
void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con )
ebaum's avatar
ebaum committed
109
{
110 111 112 113 114 115 116 117 118
    size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
                              WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() )
    );
    // well ... we want it to be unique in both:
    c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
                        WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() )
    );

    // if there already is one ... exception
119
    if( c )
120
    {
121
        throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
122
    }
123 124

    m_inputConnectors.push_back( con );
ebaum's avatar
ebaum committed
125 126
}

Alexander Wiebel's avatar
Alexander Wiebel committed
127
void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con )
ebaum's avatar
ebaum committed
128
{
129 130 131 132 133 134 135 136 137
    size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
                              WPredicateHelper::Name< boost::shared_ptr< WModuleInputConnector > >( con->getName() )
    );
    // well ... we want it to be unique in both:
    c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
                        WPredicateHelper::Name< boost::shared_ptr< WModuleOutputConnector > >( con->getName() )
    );

    // if there already is one ... exception
138
    if( c )
139
    {
140
        throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
141
    }
142 143

    m_outputConnectors.push_back( con );
ebaum's avatar
ebaum committed
144 145
}

146
void WModule::disconnect()
147
{
148
    // remove connections and their signals
149
    for( InputConnectorList::iterator listIter = m_inputConnectors.begin();
Alexander Wiebel's avatar
Alexander Wiebel committed
150
         listIter != m_inputConnectors.end(); ++listIter )
151 152 153
    {
        ( *listIter )->disconnectAll();
    }
154
    for( OutputConnectorList::iterator listIter = m_outputConnectors.begin();
Alexander Wiebel's avatar
Alexander Wiebel committed
155
         listIter != m_outputConnectors.end(); ++listIter )
156 157 158
    {
        ( *listIter )->disconnectAll();
    }
159 160
}

161 162 163 164 165 166 167 168 169 170 171
WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections()
{
    WCombinerTypes::WDisconnectList discons;

    // iterate inputs
    for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter )
    {
        // get all connections of the current connector:
        WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
                                                                               ( *listIter )->getPossibleDisconnections() );

172
        if( g.second.size() )
173 174 175 176 177 178 179 180 181 182 183 184
        {
            discons.push_back( g );
        }
    }

    // iterate outputs
    for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter )
    {
        // get all connections of the current connector:
        WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
                                                                               ( *listIter )->getPossibleDisconnections() );

185
        if( g.second.size() )
186 187 188 189 190 191 192 193
        {
            discons.push_back( g );
        }
    }

    return discons;
}

194 195
void WModule::removeConnectors()
{
196 197
    m_initialized( false );
    m_isUsable( m_initialized() && m_isAssociated() );
198

199
    // remove connections and their signals, this is flat removal. The module container can do deep removal
200
    disconnect();
201 202 203

    // clean up list
    // this should delete the connector since nobody else *should* have another shared_ptr to them
Alexander Wiebel's avatar
Alexander Wiebel committed
204 205
    m_inputConnectors.clear();
    m_outputConnectors.clear();
206 207
}

208 209 210 211
void WModule::connectors()
{
}

212 213 214 215
void WModule::properties()
{
}

216 217 218 219
void WModule::requirements()
{
}

220 221 222 223
void WModule::activate()
{
}

224
void WModule::initialize()
225
{
226
    // doing it twice is not allowed
227
    if( isInitialized()() )
228
    {
229 230
        throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() +
                                          std::string( ". Reason: already initialized." ) );
231 232
    }

233 234 235 236
    // set the module name as default runtime name
    m_runtimeName->set( getName() );

    // initialize connectors and properties
237
    requirements();
238
    connectors();
239
    properties();
240

241
    // now, the module is initialized but not necessarily usable (if not associated with a container)
242 243
    m_initialized( true );
    m_isUsable( m_initialized() && m_isAssociated() );
244 245
}

246 247 248 249 250 251
void WModule::cleanup()
{
    // currently just removes connectors
    removeConnectors();
}

252 253 254 255 256
boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const
{
    return m_container;
}

257 258 259
void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container )
{
    m_container = container;
260 261 262 263

    // true if the pointer is set
    m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() );
    m_isUsable( m_initialized() && m_isAssociated() );
264 265
}

266 267 268 269 270
MODULE_TYPE WModule::getType() const
{
    return MODULE_ARBITRARY;
}

271
const WModule::InputConnectorList& WModule::getInputConnectors() const
272
{
Alexander Wiebel's avatar
Alexander Wiebel committed
273
    return m_inputConnectors;
274 275
}

276
const WModule::OutputConnectorList& WModule::getOutputConnectors() const
277
{
Alexander Wiebel's avatar
Alexander Wiebel committed
278
    return m_outputConnectors;
279 280
}

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
281
boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name )
282 283
{
    // simply search
284
    for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin();
285 286 287
         listIter != m_inputConnectors.end(); ++listIter )
    {
        // try the canonical name
288
        if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
289 290 291 292 293
        {
            return ( *listIter );
        }
    }

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
294
    return boost::shared_ptr< WModuleInputConnector >();
295 296
}

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
297 298 299 300
boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name )
{
    boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name );

301
    if( !p )
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
302
    {
303 304
        throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
                                        std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) );
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
305 306 307 308 309 310
    }

    return p;
}

boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name )
311 312
{
    // simply search
313
    for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin();
314 315 316
         listIter != m_outputConnectors.end(); ++listIter )
    {
        // try the canonical name
317
        if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
318 319 320 321 322
        {
            return ( *listIter );
        }
    }

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
323 324 325 326 327 328 329
    return boost::shared_ptr< WModuleOutputConnector >();
}

boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name )
{
    boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name );

330
    if( !p )
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
331
    {
332 333 334
        throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
                                        std::string( "\" does not exist in the module \"" ) + getName() +
                                        std::string( "\"." ) );
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
335 336 337 338 339 340 341 342 343
    }

    return p;
}

boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name )
{
    // simply search both
    boost::shared_ptr< WModuleConnector > p = findInputConnector( name );
344
    if( p ) // found?
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
345 346 347 348 349 350 351 352 353 354 355 356
    {
        return p;
    }

    // search in output list
    return findOutputConnector( name );
}

boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name )
{
    boost::shared_ptr< WModuleConnector > p = findConnector( name );

357
    if( !p )
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
358
    {
359 360 361
        throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
                                        std::string( "\" does not exist in the module \"" ) + getName() +
                                        std::string( "\"." ) );
Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
362 363 364
    }

    return p;
365 366
}

367
boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier )
368
{
369
    switch ( signal )
370
    {
371
        case WM_READY:
372 373 374 375 376 377 378 379 380 381 382 383 384
            return signal_ready.connect( notifier );
        default:
            std::ostringstream s;
            s << "Could not subscribe to unknown signal.";
            throw WModuleSignalSubscriptionFailed( s.str() );
            break;
    }
}

boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier )
{
    switch (signal)
    {
385
        case WM_ERROR:
386
            return signal_error.connect( notifier );
387 388 389 390 391 392
        default:
            std::ostringstream s;
            s << "Could not subscribe to unknown signal.";
            throw WModuleSignalSubscriptionFailed( s.str() );
            break;
    }
393 394
}

395 396 397 398 399
const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
{
    switch ( signal )
    {
        case CONNECTION_ESTABLISHED:
400
            return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 );
401
        case CONNECTION_CLOSED:
402
            return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 );
403
        case DATA_CHANGED:
404
            return boost::bind( &WModule::notifyDataChange, this, _1, _2 );
405
        default:
406 407
            std::ostringstream s;
            s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module.";
408
            throw WModuleSignalUnknown( s.str() );
409 410 411 412
            break;
    }
}

413
const WBoolFlag&  WModule::isInitialized() const
414
{
415
    return m_initialized;
416 417
}

418
const WBoolFlag& WModule::isAssociated() const
419
{
420
    return m_isAssociated;
421 422
}

423
const WBoolFlag& WModule::isUseable() const
424
{
425 426
    return m_isUsable;
    //return isInitialized() && isAssociated();
427 428
}

429 430 431 432 433
const WBoolFlag& WModule::isReady() const
{
    return m_isReady;
}

434 435 436 437 438 439 440 441 442 443
const WBoolFlag& WModule::isCrashed() const
{
    return m_isCrashed;
}

const WBoolFlag& WModule::isReadyOrCrashed() const
{
    return m_isReadyOrCrashed;
}

444 445 446 447 448
const WBoolFlag& WModule::isRunning() const
{
    return m_isRunning;
}

Alexander Wiebel's avatar
Alexander Wiebel committed
449 450
void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
                                           boost::shared_ptr< WModuleConnector > /*there*/ )
451 452 453 454
{
    // By default this callback does nothing. Overwrite it in your module.
}

Alexander Wiebel's avatar
Alexander Wiebel committed
455 456
void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
                                      boost::shared_ptr< WModuleConnector > /*there*/ )
457 458 459 460
{
    // By default this callback does nothing. Overwrite it in your module.
}

Alexander Wiebel's avatar
Alexander Wiebel committed
461 462
void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/,
                                boost::shared_ptr< WModuleConnector > /*output*/ )
463 464 465 466
{
    // By default this callback does nothing. Overwrite it in your module.
}

467
boost::shared_ptr< WProperties > WModule::getProperties() const
468
{
469
    return m_properties;
470 471
}

472 473 474 475 476
boost::shared_ptr< WProperties > WModule::getInformationProperties() const
{
    return m_infoProperties;
}

477 478 479 480 481
boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner()
{
    return m_progress;
}

482 483
const char** WModule::getXPMIcon() const
{
484
    // return empty 1x1 icon by default.
485 486
    static const char * o_xpm[] =
        {
487
            "1 1 1 1",
488
            "   c None",
489
            " "
490 491 492 493
        };
    return o_xpm;
}

494 495
void WModule::ready()
{
496
    m_isReady( true );
497
    m_readyProgress->finish();
498
    signal_ready( shared_from_this() );
499 500
}

501 502 503
const WRequirement* WModule::checkRequirements() const
{
    // simply iterate all requirements and return the first found that is not fulfilled
504
    for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i )
505
    {
506
        if( !( *i )->isComplied() )
507 508 509 510 511 512 513 514
        {
            return *i;
        }
    }

    return NULL;
}

515 516
void WModule::threadMain()
{
517
#ifdef __linux__
518
    // set the name of the thread. This name is shown by the "top", for example.
519
    prctl( PR_SET_NAME, ( "openwalnut (" + getName() + ")" ).c_str() );
520 521
#endif

522 523
    try
    {
524
        WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO );
525

526 527
        // check requirements
        const WRequirement* failedReq = checkRequirements();
528
        if( failedReq )
529 530 531 532
        {
            throw WModuleRequirementNotMet( failedReq );
        }

533
        // call main thread function
534
        m_isRunning( true );
535 536
        moduleMain();
    }
537
    catch( const WException& e )
538
    {
539
        wlog::error( "Module (" + getName() +")" ) << "WException. Notifying. Message: " << e.what();
540

541 542
        // ensure proper exception propagation
        signal_error( shared_from_this(), e );
543

544 545
        // hopefully, all waiting threads use isReadyOrCrashed to wait.
        m_isCrashed( true );
546
    }
547
    catch( const std::exception& e )
548 549 550
    {
        // convert these exceptions to WException
        WException ce = WException( e );
551 552 553 554 555

        // print this message AFTER creation of WException to have the backtrace before the message
        WLogger::getLogger()->addLogMessage( std::string( "Exception. Notifying.  Message: " ) + e.what(), "Module (" + getName() + ")", LL_ERROR );

        // communicate error
556
        signal_error( shared_from_this(), ce );
557

558 559
        // hopefully, all waiting threads use isReadyOrCrashed to wait.
        m_isCrashed( true );
560
    }
561

562 563 564
    // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get
    // disconnected in the connector destructor.
    disconnect();
565
    m_isRunning( false );
566 567
}

568
wlog::WStreamedLogger WModule::infoLog() const
569 570 571 572
{
    return wlog::info( getName() );
}

573
wlog::WStreamedLogger WModule::errorLog() const
574 575 576 577
{
    return wlog::error( getName() );
}

578
wlog::WStreamedLogger WModule::debugLog() const
579 580 581 582
{
    return wlog::debug( getName() );
}

583
wlog::WStreamedLogger WModule::warnLog() const
584 585 586
{
    return wlog::warn( getName() );
}
587 588 589 590 591 592 593 594 595 596 597

void WModule::setLocalPath( boost::filesystem::path path )
{
    m_localPath = path;
}

boost::filesystem::path WModule::getLocalPath() const
{
    return m_localPath;
}