//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// 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 .
//
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include "WModule.h"
#include "WModuleContainer.h"
#include "exceptions/WModuleConnectionFailed.h"
#include "exceptions/WModuleConnectionInvalid.h"
#include "exceptions/WModuleDisconnectFailed.h"
#include "exceptions/WModuleSignalSubscriptionFailed.h"
#include "exceptions/WModuleConnectorsIncompatible.h"
#include "WModuleConnectorSignals.h"
#include "WModuleConnector.h"
WModuleConnector::WModuleConnector( boost::shared_ptr< WModule > module, std::string name, std::string description ):
boost::enable_shared_from_this()
{
// initialize members
m_module = module;
m_moduleName = module->getName();
m_name = name;
m_description = description;
// connect standard signals
// NOTE: these signals are NOT emitted by the connector this one is connected to, since a module can't send a "connection
// closed" message if the connection is closed.
subscribeSignal( CONNECTION_ESTABLISHED, boost::bind( &WModuleConnector::notifyConnectionEstablished, this, _1, _2 ) );
subscribeSignal( CONNECTION_CLOSED, boost::bind( &WModuleConnector::notifyConnectionClosed, this, _1, _2 ) );
signal_ConnectionEstablished.connect( getSignalHandler( CONNECTION_ESTABLISHED ) );
signal_ConnectionClosed.connect( getSignalHandler( CONNECTION_CLOSED ) );
}
WModuleConnector::~WModuleConnector()
{
disconnectAll();
// cleanup
signal_ConnectionEstablished.disconnect_all_slots();
signal_ConnectionClosed.disconnect_all_slots();
}
bool WModuleConnector::isConnectedTo( boost::shared_ptr con )
{
boost::shared_lock slock;
slock = boost::shared_lock( m_connectionListLock );
int c1 = m_connected.count( con );
slock.unlock();
slock = boost::shared_lock( con->m_connectionListLock );
int c2 = con->m_connected.count( shared_from_this() );
slock.unlock();
// if the count is different the connection is invalid
if ( c1 != c2 )
{
std::ostringstream s;
s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
throw WModuleConnectionInvalid( s.str() );
}
return ( c1 == 1 );
}
unsigned int WModuleConnector::isConnected()
{
boost::shared_lock slock = boost::shared_lock( m_connectionListLock );
int count = m_connected.size();
slock.unlock();
return count;
}
void WModuleConnector::connect( boost::shared_ptr con )
{
boost::shared_ptr< WModule > module = m_module;//.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
boost::shared_ptr< WModuleContainer > container = module->getAssociatedContainer();
std::string containerName = container.get() ? container->getName() : "Unknown";
WLogger::getLogger()->addLogMessage( "Connecting " + con->getCanonicalName() + " with " + getCanonicalName(),
"ModuleContainer (" + containerName + ")", LL_INFO );
// are both partners compatible to each other?
if ( !( con->connectable( shared_from_this() ) && connectable( con ) ) )
{
std::ostringstream s;
s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
throw WModuleConnectorsIncompatible( s.str() );
}
// check whether they are already connected
if ( isConnectedTo( con ) )
{
// is this worth an exception?
return;
}
boost::unique_lock lock;
try
{
// add to list
lock = boost::unique_lock( m_connectionListLock );
m_connected.insert( con );
lock.unlock();
// add to list of partner
lock = boost::unique_lock( con->m_connectionListLock );
con->m_connected.insert( shared_from_this() );
lock.unlock();
}
catch( const std::exception& e )
{
lock.unlock();
// undo changes
m_connected.erase( con );
con->m_connected.erase( con );
std::ostringstream s;
s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
throw WModuleConnectionFailed( s.str() );
}
catch( const boost::exception& e )
{
lock.unlock();
// undo changes
m_connected.erase( con );
con->m_connected.erase( con );
std::ostringstream s;
s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
throw WModuleConnectionFailed( s.str() );
}
// let them connect their signals
connectSignals( con );
con->connectSignals( shared_from_this() );
// signal "connection established"
signal_ConnectionEstablished( shared_from_this(), con );
// signal to my partner, of course with the parameters the other way round
con->signal_ConnectionEstablished( con, shared_from_this() );
}
void WModuleConnector::connectSignals( boost::shared_ptr /*con*/ )
{
// Add extra signal- connections here that are COMMON to ALL connectors.
// NOTE: connection established and connection closed are not signals to connect, since you can not send an connection closed
// signal to somebody with whom you are not connected anymore ;-).
}
void WModuleConnector::disconnectSignals( boost::shared_ptr /*con*/ )
{
// The base module does not subscribe to any signal -> no disconnection needed here
}
boost::signals2::connection WModuleConnector::subscribeSignal( MODULE_CONNECTOR_SIGNAL signal,
t_GenericSignalHandlerType notifier )
{
switch (signal)
{
case CONNECTION_ESTABLISHED:
return signal_ConnectionEstablished.connect( notifier );
case CONNECTION_CLOSED:
return signal_ConnectionClosed.connect( notifier );
default:
std::ostringstream s;
s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly.";
throw WModuleSignalSubscriptionFailed( s.str() );
break;
}
}
const t_GenericSignalHandlerType WModuleConnector::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
{
// the module instance knows that
boost::shared_ptr< WModule > module = m_module;//.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
return module->getSignalHandler( signal );
}
void WModuleConnector::disconnect( boost::shared_ptr con, bool removeFromOwnList )
{
if ( !isConnectedTo( con ) )
{
return;
}
// write lock
boost::unique_lock lock;
try
{
// disconnect all signals
con->disconnectSignals( shared_from_this() );
disconnectSignals( con );
// remove from list
if ( removeFromOwnList )
{
lock = boost::unique_lock( m_connectionListLock );
// since we use shared pointers, erasing the item should be enough
m_connected.erase( con );
lock.unlock();
}
// remove me from his list
lock = boost::unique_lock( con->m_connectionListLock );
con->m_connected.erase( shared_from_this() );
lock.unlock();
// signal closed connection
signal_ConnectionClosed( shared_from_this(), con );
con->signal_ConnectionClosed( shared_from_this(), con );
}
catch( const std::exception& e )
{
lock.unlock();
std::ostringstream s;
s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
throw WModuleDisconnectFailed( s.str() );
}
catch( const boost::exception& e )
{
lock.unlock();
std::ostringstream s;
s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
throw WModuleDisconnectFailed( s.str() );
}
}
void WModuleConnector::disconnectAll()
{
// remove from list
// acquire read lock
boost::shared_lock rlock( m_connectionListLock );
// each connector needs to be notified and disconnected properly
for( std::set >::iterator listIter = m_connected.begin(); listIter != m_connected.end();
++listIter )
{
disconnect( *listIter, false );
}
rlock.unlock();
// lock it for writing
boost::unique_lock lock( m_connectionListLock );
m_connected.clear();
lock.unlock();
}
const std::string WModuleConnector::getDescription() const
{
return m_description;
}
const std::string WModuleConnector::getName() const
{
return m_name;
}
const std::string WModuleConnector::getCanonicalName() const
{
std::ostringstream s;
s << m_moduleName << ":" << getName();
return s.str();
}
void WModuleConnector::setDescription( std::string desc )
{
m_description = desc;
}
void WModuleConnector::setName( std::string name )
{
m_name = name;
}
void WModuleConnector::notifyConnectionEstablished( boost::shared_ptr /*here*/, boost::shared_ptr /*there*/ )
{
// by default: do nothing.
}
void WModuleConnector::notifyConnectionClosed( boost::shared_ptr /*here*/, boost::shared_ptr /*there*/ )
{
// do nothing by default
}