OpenWalnut  1.4.0
WModuleConnector.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 #include <iostream>
00026 #include <list>
00027 #include <string>
00028 #include <sstream>
00029 #include <set>
00030 
00031 #include <boost/version.hpp>
00032 #if ( BOOST_VERSION >= 104200 ) // exception.hpp is deprecated in Boost 1.42
00033     #include <boost/exception/all.hpp>
00034 #else
00035     #include <boost/exception.hpp>
00036 #endif
00037 
00038 #include <boost/signals2/signal.hpp>
00039 #include <boost/signals2/connection.hpp>
00040 
00041 #include "../common/exceptions/WSignalSubscriptionFailed.h"
00042 #include "WModule.h"
00043 #include "WModuleConnectorSignals.h"
00044 #include "WModuleContainer.h"
00045 #include "WModuleInputConnector.h"
00046 #include "WModuleOutputConnector.h"
00047 #include "combiner/WDisconnectCombiner.h"
00048 #include "exceptions/WModuleConnectionFailed.h"
00049 #include "exceptions/WModuleConnectionInvalid.h"
00050 #include "exceptions/WModuleConnectorsIncompatible.h"
00051 #include "exceptions/WModuleDisconnectFailed.h"
00052 #include "exceptions/WModuleConnectorModuleLockFailed.h"
00053 
00054 #include "WModuleConnector.h"
00055 
00056 WModuleConnector::WModuleConnector( boost::shared_ptr< WModule > module, std::string name, std::string description ):
00057     boost::enable_shared_from_this<WModuleConnector>()
00058 {
00059     // initialize members
00060     m_module = module;
00061     m_moduleName = module->getName();
00062 
00063     m_name = name;
00064     m_description = description;
00065 
00066     // connect standard signals
00067     // NOTE: these signals are NOT emitted by the connector this one is connected to, since a module can't send a "connection
00068     // closed" message if the connection is closed.
00069     subscribeSignal( CONNECTION_ESTABLISHED, boost::bind( &WModuleConnector::notifyConnectionEstablished, this, _1, _2 ) );
00070     subscribeSignal( CONNECTION_CLOSED, boost::bind( &WModuleConnector::notifyConnectionClosed, this, _1, _2 ) );
00071 
00072     signal_ConnectionEstablished.connect( getSignalHandler( CONNECTION_ESTABLISHED ) );
00073     signal_ConnectionClosed.connect( getSignalHandler( CONNECTION_CLOSED ) );
00074 }
00075 
00076 WModuleConnector::~WModuleConnector()
00077 {
00078     disconnectAll();
00079 
00080     // cleanup
00081     signal_ConnectionEstablished.disconnect_all_slots();
00082     signal_ConnectionClosed.disconnect_all_slots();
00083 }
00084 
00085 bool WModuleConnector::isConnectedTo( boost::shared_ptr<WModuleConnector> con )
00086 {
00087     boost::shared_lock<boost::shared_mutex> slock;
00088     slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
00089     int c1 = m_connected.count( con );
00090     slock.unlock();
00091 
00092     slock = boost::shared_lock<boost::shared_mutex>( con->m_connectionListLock );
00093     int c2 = con->m_connected.count( shared_from_this() );
00094     slock.unlock();
00095 
00096     // if the count is different the connection is invalid
00097     if( c1 != c2 )
00098     {
00099         std::ostringstream s;
00100         s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
00101         throw WModuleConnectionInvalid( s.str() );
00102     }
00103 
00104     return ( c1 == 1 );
00105 }
00106 
00107 unsigned int WModuleConnector::isConnected()
00108 {
00109     boost::shared_lock<boost::shared_mutex> slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
00110     int count = m_connected.size();
00111     slock.unlock();
00112     return count;
00113 }
00114 
00115 void WModuleConnector::connect( boost::shared_ptr<WModuleConnector> con )
00116 {
00117     boost::shared_ptr< WModule > module = m_module.lock();    // it is "unlocked" at the end of this function as "module" looses its scope
00118     std::string containerName = "Unknown";
00119     if( module )
00120     {
00121         boost::shared_ptr< WModuleContainer > container;
00122         container = module->getAssociatedContainer();
00123         containerName = container.get() ? container->getName() : "Unknown";
00124     }
00125     WLogger::getLogger()->addLogMessage( "Connecting " + con->getCanonicalName() + " with " + getCanonicalName(),
00126                                          "ModuleContainer (" + containerName + ")", LL_INFO );
00127 
00128     // are both partners compatible to each other?
00129     if( !( con->connectable( shared_from_this() ) && connectable( con ) ) )
00130     {
00131         std::ostringstream s;
00132         s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
00133         throw WModuleConnectorsIncompatible( s.str() );
00134     }
00135 
00136     // check whether they are already connected
00137     if( isConnectedTo( con ) )
00138     {
00139         WLogger::getLogger()->addLogMessage( con->getCanonicalName() + " and " + getCanonicalName() + " are already connected.",
00140                                              "ModuleContainer (" + containerName + ")", LL_INFO );
00141         return;
00142     }
00143 
00144     boost::unique_lock<boost::shared_mutex> lock;
00145     boost::unique_lock<boost::shared_mutex> lockRemote;
00146     try
00147     {
00148         // get locks
00149         lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
00150         lockRemote = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
00151 
00152         // is the input connected already?
00153         if( ( isInputConnector() && m_connected.size() ) || ( con->isInputConnector() && con->m_connected.size() ) )
00154         {
00155             throw WModuleConnectionFailed( std::string( "Input connector already connected. Disconnect it first." ) );
00156         }
00157 
00158         m_connected.insert( con );
00159         con->m_connected.insert( shared_from_this() );
00160 
00161         lock.unlock();
00162         lockRemote.unlock();
00163     }
00164     catch( const WException& e )
00165     {
00166         lock.unlock();
00167         lockRemote.unlock();
00168 
00169         // undo changes
00170         m_connected.erase( con );
00171         con->m_connected.erase( con );
00172 
00173         throw e;
00174     }
00175     catch( const std::exception& e )
00176     {
00177         lock.unlock();
00178         lockRemote.unlock();
00179 
00180         // undo changes
00181         m_connected.erase( con );
00182         con->m_connected.erase( con );
00183 
00184         std::ostringstream s;
00185         s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
00186         throw WModuleConnectionFailed( s.str() );
00187     }
00188     catch( const boost::exception& e )
00189     {
00190         lock.unlock();
00191         lockRemote.unlock();
00192 
00193         // undo changes
00194         m_connected.erase( con );
00195         con->m_connected.erase( con );
00196 
00197         std::ostringstream s;
00198         s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
00199         throw WModuleConnectionFailed( s.str() );
00200     }
00201 
00202     // let them connect their signals
00203     connectSignals( con );
00204     con->connectSignals( shared_from_this() );
00205 
00206     // signal "connection established"
00207     signal_ConnectionEstablished( shared_from_this(), con );
00208     // signal to my partner, of course with the parameters the other way round
00209     con->signal_ConnectionEstablished( con, shared_from_this() );
00210 }
00211 
00212 void WModuleConnector::connectSignals( boost::shared_ptr<WModuleConnector> /*con*/ )
00213 {
00214     // Add extra signal- connections here that are COMMON to ALL connectors.
00215     // NOTE: connection established and connection closed are not signals to connect, since you can not send an connection closed
00216     // signal to somebody with whom you are not connected anymore ;-).
00217 }
00218 
00219 void WModuleConnector::disconnectSignals( boost::shared_ptr<WModuleConnector> /*con*/ )
00220 {
00221     // The base module does not subscribe to any signal -> no disconnection needed here
00222 }
00223 
00224 boost::signals2::connection WModuleConnector::subscribeSignal( MODULE_CONNECTOR_SIGNAL signal,
00225                                                                t_GenericSignalHandlerType notifier )
00226 {
00227     switch( signal)
00228     {
00229         case CONNECTION_ESTABLISHED:
00230             return signal_ConnectionEstablished.connect( notifier );
00231         case CONNECTION_CLOSED:
00232             return signal_ConnectionClosed.connect( notifier );
00233         default:
00234             std::ostringstream s;
00235             s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly.";
00236             throw WSignalSubscriptionFailed( s.str() );
00237             break;
00238     }
00239 }
00240 
00241 const t_GenericSignalHandlerType WModuleConnector::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
00242 {
00243     // the module instance knows that
00244     boost::shared_ptr< WModule > module = m_module.lock();    // it is "unlocked" at the end of this function as "module" looses its scope
00245     if( !module )
00246     {
00247         throw WModuleConnectorModuleLockFailed();
00248     }
00249     return module->getSignalHandler( signal );
00250 }
00251 
00252 boost::shared_ptr< WModule > WModuleConnector::getModule() const
00253 {
00254     return m_module.lock();    // it is "unlocked" at the end of this function as "module" looses its scope
00255 }
00256 
00257 void WModuleConnector::disconnect( boost::shared_ptr<WModuleConnector> con, bool removeFromOwnList )
00258 {
00259     boost::shared_ptr< WModule > module = m_module.lock();    // it is "unlocked" at the end of this function as "module" looses its scope
00260     std::string containerName = "Unknown";
00261     if( module )
00262     {
00263         boost::shared_ptr< WModuleContainer > container;
00264         container = module->getAssociatedContainer();
00265         containerName = container.get() ? container->getName() : "Unknown";
00266     }
00267 
00268     if( !isConnectedTo( con ) )
00269     {
00270         WLogger::getLogger()->addLogMessage( "Could not disconnect " + con->getCanonicalName() + " from " + getCanonicalName() + " as they are"+
00271                                              " not connected.", "ModuleContainer (" + containerName + ")", LL_INFO );
00272         return;
00273     }
00274 
00275     WLogger::getLogger()->addLogMessage( "Disconnecting " + con->getCanonicalName() + " from " + getCanonicalName(),
00276                                          "ModuleContainer (" + containerName + ")", LL_INFO );
00277 
00278     // write lock
00279     boost::unique_lock<boost::shared_mutex> lock;
00280     try
00281     {
00282         // disconnect all signals
00283         con->disconnectSignals( shared_from_this() );
00284         disconnectSignals( con );
00285 
00286         // remove from list
00287         if( removeFromOwnList )
00288         {
00289             lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
00290             // since we use shared pointers, erasing the item should be enough
00291             m_connected.erase( con );
00292             lock.unlock();
00293         }
00294 
00295         // remove me from his list
00296         lock = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
00297         con->m_connected.erase( shared_from_this() );
00298         lock.unlock();
00299 
00300         // signal "closed connection"
00301         // NOTE: at this point, there might be an connected input connector even though we disconnected it. This is because of removeFromOwnList.
00302         // The input connectors handle this with an additional member variable denoting their disconnect state
00303         signal_ConnectionClosed( shared_from_this(), con );
00304         con->signal_ConnectionClosed( shared_from_this(), con );
00305     }
00306     catch( const std::exception& e )
00307     {
00308         lock.unlock();
00309 
00310         std::ostringstream s;
00311         s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
00312         throw WModuleDisconnectFailed( s.str() );
00313     }
00314     catch( const boost::exception& e )
00315     {
00316         lock.unlock();
00317 
00318         std::ostringstream s;
00319         s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
00320         throw WModuleDisconnectFailed( s.str() );
00321     }
00322 }
00323 
00324 void WModuleConnector::disconnectAll()
00325 {
00326     // remove from list
00327 
00328     // acquire read lock
00329     boost::shared_lock<boost::shared_mutex> rlock( m_connectionListLock );
00330 
00331     // each connector needs to be notified and disconnected properly
00332     for( std::set<boost::shared_ptr<WModuleConnector> >::iterator listIter = m_connected.begin(); listIter != m_connected.end();
00333          ++listIter )
00334     {
00335         disconnect( *listIter, false );
00336     }
00337     rlock.unlock();
00338 
00339     // lock it for writing
00340     boost::unique_lock<boost::shared_mutex> lock( m_connectionListLock );
00341     m_connected.clear();
00342     lock.unlock();
00343 }
00344 
00345 const std::string WModuleConnector::getDescription() const
00346 {
00347     return m_description;
00348 }
00349 
00350 const std::string WModuleConnector::getName() const
00351 {
00352     return m_name;
00353 }
00354 
00355 const std::string WModuleConnector::getCanonicalName() const
00356 {
00357     std::ostringstream s;
00358     s << m_moduleName << ":" << getName();
00359 
00360     return s.str();
00361 }
00362 
00363 void WModuleConnector::setDescription( std::string desc )
00364 {
00365     m_description = desc;
00366 }
00367 
00368 void WModuleConnector::setName( std::string name )
00369 {
00370     m_name = name;
00371 }
00372 
00373 WCombinerTypes::WOneToOneCombiners WModuleConnector::getPossibleDisconnections()
00374 {
00375     WCombinerTypes::WOneToOneCombiners l;
00376 
00377     // acquire read lock
00378     boost::shared_lock<boost::shared_mutex> rlock( m_connectionListLock );
00379 
00380     // for each connector
00381     for( std::set<boost::shared_ptr<WModuleConnector> >::iterator listIter = m_connected.begin(); listIter != m_connected.end(); ++listIter )
00382     {
00383         // simply create the combiner
00384         l.push_back( boost::shared_ptr< WDisconnectCombiner >( new WDisconnectCombiner( shared_from_this(), ( *listIter ) ) ) );
00385     }
00386     rlock.unlock();
00387 
00388     return l;
00389 }
00390 
00391 void WModuleConnector::notifyConnectionEstablished( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
00392 {
00393     // by default: do nothing.
00394 }
00395 
00396 void WModuleConnector::notifyConnectionClosed( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
00397 {
00398     // do nothing by default
00399 }
00400 
00401 boost::shared_ptr< WModuleInputConnector > WModuleConnector::toInputConnector()
00402 {
00403     return boost::dynamic_pointer_cast< WModuleInputConnector >( shared_from_this() );
00404 }
00405 
00406 boost::shared_ptr< WModuleOutputConnector > WModuleConnector::toOutputConnector()
00407 {
00408     return boost::dynamic_pointer_cast< WModuleOutputConnector >( shared_from_this() );
00409 }
00410