WModuleConnector.cpp 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
//---------------------------------------------------------------------------
//
// 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 <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------

#include <iostream>
#include <list>
27
#include <string>
28
#include <sstream>
29
#include <set>
30

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
31 32 33
#include <boost/exception.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/signals2/connection.hpp>
34 35

#include "WModule.h"
36
#include "WModuleContainer.h"
37
#include "exceptions/WModuleConnectionFailed.h"
38
#include "exceptions/WModuleConnectionInvalid.h"
39 40
#include "exceptions/WModuleDisconnectFailed.h"
#include "exceptions/WModuleSignalSubscriptionFailed.h"
41
#include "exceptions/WModuleConnectorsIncompatible.h"
42
#include "WModuleConnectorSignals.h"
43 44

#include "WModuleConnector.h"
45

Sebastian Eichelbaum's avatar
[STYLE]  
Sebastian Eichelbaum committed
46
WModuleConnector::WModuleConnector( boost::shared_ptr< WModule > module, std::string name, std::string description ):
47
    boost::enable_shared_from_this<WModuleConnector>()
48 49
{
    // initialize members
50
    m_module = module;
51
    m_moduleName = module->getName();
52

53 54
    m_name = name;
    m_description = description;
55 56 57 58

    // 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.
59 60 61
    subscribeSignal( CONNECTION_ESTABLISHED, boost::bind( &WModuleConnector::notifyConnectionEstablished, this, _1, _2 ) );
    subscribeSignal( CONNECTION_CLOSED, boost::bind( &WModuleConnector::notifyConnectionClosed, this, _1, _2 ) );

62 63
    signal_ConnectionEstablished.connect( getSignalHandler( CONNECTION_ESTABLISHED ) );
    signal_ConnectionClosed.connect( getSignalHandler( CONNECTION_CLOSED ) );
64 65 66 67 68
}

WModuleConnector::~WModuleConnector()
{
    disconnectAll();
69

70
    // cleanup
71 72
    signal_ConnectionEstablished.disconnect_all_slots();
    signal_ConnectionClosed.disconnect_all_slots();
73 74
}

75 76 77
bool WModuleConnector::isConnectedTo( boost::shared_ptr<WModuleConnector> con )
{
    boost::shared_lock<boost::shared_mutex> slock;
78 79
    slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
    int c1 = m_connected.count( con );
80 81
    slock.unlock();

82 83
    slock = boost::shared_lock<boost::shared_mutex>( con->m_connectionListLock );
    int c2 = con->m_connected.count( shared_from_this() );
84 85 86 87 88 89 90 91 92 93 94 95 96
    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 );
}

97 98 99 100 101 102 103 104
unsigned int WModuleConnector::isConnected()
{
    boost::shared_lock<boost::shared_mutex> slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
    int count = m_connected.size();
    slock.unlock();
    return count;
}

105
void WModuleConnector::connect( boost::shared_ptr<WModuleConnector> con )
106
{
107 108
    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();
109
    std::string containerName = container.get() ? container->getName() : "Unknown";
110 111
    WLogger::getLogger()->addLogMessage( "Connecting " + con->getCanonicalName() + " with " + getCanonicalName(),
                                         "ModuleContainer (" + containerName + ")", LL_INFO );
112

113 114 115 116 117
    // 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.";
118
        throw WModuleConnectorsIncompatible( s.str() );
119
    }
120

121 122 123 124 125 126 127
    // check whether they are already connected
    if ( isConnectedTo( con ) )
    {
        // is this worth an exception?
        return;
    }

128 129 130 131
    boost::unique_lock<boost::shared_mutex> lock;
    try
    {
        // add to list
132 133
        lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
        m_connected.insert( con );
134 135 136
        lock.unlock();

        // add to list of partner
137 138
        lock = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
        con->m_connected.insert( shared_from_this() );
139 140
        lock.unlock();
    }
141
    catch( const std::exception& e )
142 143 144 145
    {
        lock.unlock();

        // undo changes
146 147
        m_connected.erase( con );
        con->m_connected.erase( con );
148

149 150
        std::ostringstream s;
        s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
151
        throw WModuleConnectionFailed( s.str() );
152
    }
153
    catch( const boost::exception& e )
154 155 156 157
    {
        lock.unlock();

        // undo changes
158 159
        m_connected.erase( con );
        con->m_connected.erase( con );
160 161 162

        std::ostringstream s;
        s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
163
        throw WModuleConnectionFailed( s.str() );
164 165 166 167 168 169 170
    }

    // let them connect their signals
    connectSignals( con );
    con->connectSignals( shared_from_this() );

    // signal "connection established"
171 172 173
    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() );
174
}
175

176 177 178 179 180
void WModuleConnector::connectSignals( boost::shared_ptr<WModuleConnector> /*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 ;-).
181 182
}

183
void WModuleConnector::disconnectSignals( boost::shared_ptr<WModuleConnector> /*con*/ )
184
{
185 186
    // The base module does not subscribe to any signal -> no disconnection needed here
}
187

188 189
boost::signals2::connection WModuleConnector::subscribeSignal( MODULE_CONNECTOR_SIGNAL signal,
                                                               t_GenericSignalHandlerType notifier )
190 191 192 193 194 195 196 197
{
    switch (signal)
    {
        case CONNECTION_ESTABLISHED:
            return signal_ConnectionEstablished.connect( notifier );
        case CONNECTION_CLOSED:
            return signal_ConnectionClosed.connect( notifier );
        default:
198 199
            std::ostringstream s;
            s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly.";
200
            throw WModuleSignalSubscriptionFailed( s.str() );
201 202 203 204 205 206 207
            break;
    }
}

const t_GenericSignalHandlerType WModuleConnector::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
{
    // the module instance knows that
208 209
    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 );
210 211 212 213
}

void WModuleConnector::disconnect( boost::shared_ptr<WModuleConnector> con, bool removeFromOwnList )
{
214 215 216 217 218 219
    if ( !isConnectedTo( con ) )
    {
        return;
    }

    // write lock
220 221 222 223 224 225
    boost::unique_lock<boost::shared_mutex> lock;
    try
    {
        // disconnect all signals
        con->disconnectSignals( shared_from_this() );
        disconnectSignals( con );
226

227
        // remove from list
228
        if ( removeFromOwnList )
229
        {
230
            lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
231
            // since we use shared pointers, erasing the item should be enough
232
            m_connected.erase( con );
233 234 235 236
            lock.unlock();
        }

        // remove me from his list
237 238
        lock = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
        con->m_connected.erase( shared_from_this() );
239 240 241 242
        lock.unlock();

        // signal closed connection
        signal_ConnectionClosed( shared_from_this(), con );
243
        con->signal_ConnectionClosed( shared_from_this(), con );
244
    }
245
    catch( const std::exception& e )
246 247 248 249 250
    {
        lock.unlock();

        std::ostringstream s;
        s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
251
        throw WModuleDisconnectFailed( s.str() );
252
    }
253
    catch( const boost::exception& e )
254 255 256 257 258
    {
        lock.unlock();

        std::ostringstream s;
        s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
259
        throw WModuleDisconnectFailed( s.str() );
260
    }
261
}
262

263 264 265
void WModuleConnector::disconnectAll()
{
    // remove from list
266 267

    // acquire read lock
268
    boost::shared_lock<boost::shared_mutex> rlock( m_connectionListLock );
269 270

    // each connector needs to be notified and disconnected properly
271
    for( std::set<boost::shared_ptr<WModuleConnector> >::iterator listIter = m_connected.begin(); listIter != m_connected.end();
272 273
         ++listIter )
    {
274
        disconnect( *listIter, false );
275
    }
276
    rlock.unlock();
277

278
    // lock it for writing
279 280
    boost::unique_lock<boost::shared_mutex> lock( m_connectionListLock );
    m_connected.clear();
281 282 283
    lock.unlock();
}

284 285
const std::string WModuleConnector::getDescription() const
{
286
    return m_description;
287 288 289 290
}

const std::string WModuleConnector::getName() const
{
291
    return m_name;
292 293
}

294 295 296
const std::string WModuleConnector::getCanonicalName() const
{
    std::ostringstream s;
297
    s << m_moduleName << ":" << getName();
298 299 300 301

    return s.str();
}

302 303
void WModuleConnector::setDescription( std::string desc )
{
304
    m_description = desc;
305 306 307 308
}

void WModuleConnector::setName( std::string name )
{
309
    m_name = name;
310 311
}

312 313 314 315 316 317 318 319 320 321
void WModuleConnector::notifyConnectionEstablished( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
{
    // by default: do nothing.
}

void WModuleConnector::notifyConnectionClosed( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
{
    // do nothing by default
}