OpenWalnut  1.4.0
WModuleConnector.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <iostream>
26 #include <list>
27 #include <string>
28 #include <sstream>
29 #include <set>
30 
31 #include <boost/version.hpp>
32 #if ( BOOST_VERSION >= 104200 ) // exception.hpp is deprecated in Boost 1.42
33  #include <boost/exception/all.hpp>
34 #else
35  #include <boost/exception.hpp>
36 #endif
37 
38 #include <boost/signals2/signal.hpp>
39 #include <boost/signals2/connection.hpp>
40 
41 #include "../common/exceptions/WSignalSubscriptionFailed.h"
42 #include "WModule.h"
43 #include "WModuleConnectorSignals.h"
44 #include "WModuleContainer.h"
45 #include "WModuleInputConnector.h"
46 #include "WModuleOutputConnector.h"
47 #include "combiner/WDisconnectCombiner.h"
48 #include "exceptions/WModuleConnectionFailed.h"
49 #include "exceptions/WModuleConnectionInvalid.h"
50 #include "exceptions/WModuleConnectorsIncompatible.h"
51 #include "exceptions/WModuleDisconnectFailed.h"
52 #include "exceptions/WModuleConnectorModuleLockFailed.h"
53 
54 #include "WModuleConnector.h"
55 
56 WModuleConnector::WModuleConnector( boost::shared_ptr< WModule > module, std::string name, std::string description ):
57  boost::enable_shared_from_this<WModuleConnector>()
58 {
59  // initialize members
60  m_module = module;
61  m_moduleName = module->getName();
62 
63  m_name = name;
64  m_description = description;
65 
66  // connect standard signals
67  // NOTE: these signals are NOT emitted by the connector this one is connected to, since a module can't send a "connection
68  // closed" message if the connection is closed.
69  subscribeSignal( CONNECTION_ESTABLISHED, boost::bind( &WModuleConnector::notifyConnectionEstablished, this, _1, _2 ) );
70  subscribeSignal( CONNECTION_CLOSED, boost::bind( &WModuleConnector::notifyConnectionClosed, this, _1, _2 ) );
71 
72  signal_ConnectionEstablished.connect( getSignalHandler( CONNECTION_ESTABLISHED ) );
73  signal_ConnectionClosed.connect( getSignalHandler( CONNECTION_CLOSED ) );
74 }
75 
77 {
78  disconnectAll();
79 
80  // cleanup
81  signal_ConnectionEstablished.disconnect_all_slots();
82  signal_ConnectionClosed.disconnect_all_slots();
83 }
84 
85 bool WModuleConnector::isConnectedTo( boost::shared_ptr<WModuleConnector> con )
86 {
87  boost::shared_lock<boost::shared_mutex> slock;
88  slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
89  int c1 = m_connected.count( con );
90  slock.unlock();
91 
92  slock = boost::shared_lock<boost::shared_mutex>( con->m_connectionListLock );
93  int c2 = con->m_connected.count( shared_from_this() );
94  slock.unlock();
95 
96  // if the count is different the connection is invalid
97  if( c1 != c2 )
98  {
99  std::ostringstream s;
100  s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
101  throw WModuleConnectionInvalid( s.str() );
102  }
103 
104  return ( c1 == 1 );
105 }
106 
108 {
109  boost::shared_lock<boost::shared_mutex> slock = boost::shared_lock<boost::shared_mutex>( m_connectionListLock );
110  int count = m_connected.size();
111  slock.unlock();
112  return count;
113 }
114 
115 void WModuleConnector::connect( boost::shared_ptr<WModuleConnector> con )
116 {
117  boost::shared_ptr< WModule > module = m_module.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
118  std::string containerName = "Unknown";
119  if( module )
120  {
121  boost::shared_ptr< WModuleContainer > container;
122  container = module->getAssociatedContainer();
123  containerName = container.get() ? container->getName() : "Unknown";
124  }
125  WLogger::getLogger()->addLogMessage( "Connecting " + con->getCanonicalName() + " with " + getCanonicalName(),
126  "ModuleContainer (" + containerName + ")", LL_INFO );
127 
128  // are both partners compatible to each other?
129  if( !( con->connectable( shared_from_this() ) && connectable( con ) ) )
130  {
131  std::ostringstream s;
132  s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
133  throw WModuleConnectorsIncompatible( s.str() );
134  }
135 
136  // check whether they are already connected
137  if( isConnectedTo( con ) )
138  {
139  WLogger::getLogger()->addLogMessage( con->getCanonicalName() + " and " + getCanonicalName() + " are already connected.",
140  "ModuleContainer (" + containerName + ")", LL_INFO );
141  return;
142  }
143 
144  boost::unique_lock<boost::shared_mutex> lock;
145  boost::unique_lock<boost::shared_mutex> lockRemote;
146  try
147  {
148  // get locks
149  lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
150  lockRemote = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
151 
152  // is the input connected already?
153  if( ( isInputConnector() && m_connected.size() ) || ( con->isInputConnector() && con->m_connected.size() ) )
154  {
155  throw WModuleConnectionFailed( std::string( "Input connector already connected. Disconnect it first." ) );
156  }
157 
158  m_connected.insert( con );
159  con->m_connected.insert( shared_from_this() );
160 
161  lock.unlock();
162  lockRemote.unlock();
163  }
164  catch( const WException& e )
165  {
166  lock.unlock();
167  lockRemote.unlock();
168 
169  // undo changes
170  m_connected.erase( con );
171  con->m_connected.erase( con );
172 
173  throw e;
174  }
175  catch( const std::exception& e )
176  {
177  lock.unlock();
178  lockRemote.unlock();
179 
180  // undo changes
181  m_connected.erase( con );
182  con->m_connected.erase( con );
183 
184  std::ostringstream s;
185  s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
186  throw WModuleConnectionFailed( s.str() );
187  }
188  catch( const boost::exception& e )
189  {
190  lock.unlock();
191  lockRemote.unlock();
192 
193  // undo changes
194  m_connected.erase( con );
195  con->m_connected.erase( con );
196 
197  std::ostringstream s;
198  s << "Connection between " << getCanonicalName() << " and " << con->getCanonicalName() << " failed.";
199  throw WModuleConnectionFailed( s.str() );
200  }
201 
202  // let them connect their signals
203  connectSignals( con );
204  con->connectSignals( shared_from_this() );
205 
206  // signal "connection established"
207  signal_ConnectionEstablished( shared_from_this(), con );
208  // signal to my partner, of course with the parameters the other way round
209  con->signal_ConnectionEstablished( con, shared_from_this() );
210 }
211 
212 void WModuleConnector::connectSignals( boost::shared_ptr<WModuleConnector> /*con*/ )
213 {
214  // Add extra signal- connections here that are COMMON to ALL connectors.
215  // NOTE: connection established and connection closed are not signals to connect, since you can not send an connection closed
216  // signal to somebody with whom you are not connected anymore ;-).
217 }
218 
219 void WModuleConnector::disconnectSignals( boost::shared_ptr<WModuleConnector> /*con*/ )
220 {
221  // The base module does not subscribe to any signal -> no disconnection needed here
222 }
223 
224 boost::signals2::connection WModuleConnector::subscribeSignal( MODULE_CONNECTOR_SIGNAL signal,
225  t_GenericSignalHandlerType notifier )
226 {
227  switch( signal)
228  {
229  case CONNECTION_ESTABLISHED:
230  return signal_ConnectionEstablished.connect( notifier );
231  case CONNECTION_CLOSED:
232  return signal_ConnectionClosed.connect( notifier );
233  default:
234  std::ostringstream s;
235  s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly.";
236  throw WSignalSubscriptionFailed( s.str() );
237  break;
238  }
239 }
240 
241 const t_GenericSignalHandlerType WModuleConnector::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
242 {
243  // the module instance knows that
244  boost::shared_ptr< WModule > module = m_module.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
245  if( !module )
246  {
248  }
249  return module->getSignalHandler( signal );
250 }
251 
252 boost::shared_ptr< WModule > WModuleConnector::getModule() const
253 {
254  return m_module.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
255 }
256 
257 void WModuleConnector::disconnect( boost::shared_ptr<WModuleConnector> con, bool removeFromOwnList )
258 {
259  boost::shared_ptr< WModule > module = m_module.lock(); // it is "unlocked" at the end of this function as "module" looses its scope
260  std::string containerName = "Unknown";
261  if( module )
262  {
263  boost::shared_ptr< WModuleContainer > container;
264  container = module->getAssociatedContainer();
265  containerName = container.get() ? container->getName() : "Unknown";
266  }
267 
268  if( !isConnectedTo( con ) )
269  {
270  WLogger::getLogger()->addLogMessage( "Could not disconnect " + con->getCanonicalName() + " from " + getCanonicalName() + " as they are"+
271  " not connected.", "ModuleContainer (" + containerName + ")", LL_INFO );
272  return;
273  }
274 
275  WLogger::getLogger()->addLogMessage( "Disconnecting " + con->getCanonicalName() + " from " + getCanonicalName(),
276  "ModuleContainer (" + containerName + ")", LL_INFO );
277 
278  // write lock
279  boost::unique_lock<boost::shared_mutex> lock;
280  try
281  {
282  // disconnect all signals
283  con->disconnectSignals( shared_from_this() );
284  disconnectSignals( con );
285 
286  // remove from list
287  if( removeFromOwnList )
288  {
289  lock = boost::unique_lock<boost::shared_mutex>( m_connectionListLock );
290  // since we use shared pointers, erasing the item should be enough
291  m_connected.erase( con );
292  lock.unlock();
293  }
294 
295  // remove me from his list
296  lock = boost::unique_lock<boost::shared_mutex>( con->m_connectionListLock );
297  con->m_connected.erase( shared_from_this() );
298  lock.unlock();
299 
300  // signal "closed connection"
301  // NOTE: at this point, there might be an connected input connector even though we disconnected it. This is because of removeFromOwnList.
302  // The input connectors handle this with an additional member variable denoting their disconnect state
303  signal_ConnectionClosed( shared_from_this(), con );
304  con->signal_ConnectionClosed( shared_from_this(), con );
305  }
306  catch( const std::exception& e )
307  {
308  lock.unlock();
309 
310  std::ostringstream s;
311  s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
312  throw WModuleDisconnectFailed( s.str() );
313  }
314  catch( const boost::exception& e )
315  {
316  lock.unlock();
317 
318  std::ostringstream s;
319  s << "Unable to disconnect " << getCanonicalName() << " from " << con->getCanonicalName() << ".";
320  throw WModuleDisconnectFailed( s.str() );
321  }
322 }
323 
325 {
326  // remove from list
327 
328  // acquire read lock
329  boost::shared_lock<boost::shared_mutex> rlock( m_connectionListLock );
330 
331  // each connector needs to be notified and disconnected properly
332  for( std::set<boost::shared_ptr<WModuleConnector> >::iterator listIter = m_connected.begin(); listIter != m_connected.end();
333  ++listIter )
334  {
335  disconnect( *listIter, false );
336  }
337  rlock.unlock();
338 
339  // lock it for writing
340  boost::unique_lock<boost::shared_mutex> lock( m_connectionListLock );
341  m_connected.clear();
342  lock.unlock();
343 }
344 
345 const std::string WModuleConnector::getDescription() const
346 {
347  return m_description;
348 }
349 
350 const std::string WModuleConnector::getName() const
351 {
352  return m_name;
353 }
354 
355 const std::string WModuleConnector::getCanonicalName() const
356 {
357  std::ostringstream s;
358  s << m_moduleName << ":" << getName();
359 
360  return s.str();
361 }
362 
363 void WModuleConnector::setDescription( std::string desc )
364 {
365  m_description = desc;
366 }
367 
368 void WModuleConnector::setName( std::string name )
369 {
370  m_name = name;
371 }
372 
373 WCombinerTypes::WOneToOneCombiners WModuleConnector::getPossibleDisconnections()
374 {
375  WCombinerTypes::WOneToOneCombiners l;
376 
377  // acquire read lock
378  boost::shared_lock<boost::shared_mutex> rlock( m_connectionListLock );
379 
380  // for each connector
381  for( std::set<boost::shared_ptr<WModuleConnector> >::iterator listIter = m_connected.begin(); listIter != m_connected.end(); ++listIter )
382  {
383  // simply create the combiner
384  l.push_back( boost::shared_ptr< WDisconnectCombiner >( new WDisconnectCombiner( shared_from_this(), ( *listIter ) ) ) );
385  }
386  rlock.unlock();
387 
388  return l;
389 }
390 
391 void WModuleConnector::notifyConnectionEstablished( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
392 {
393  // by default: do nothing.
394 }
395 
396 void WModuleConnector::notifyConnectionClosed( boost::shared_ptr<WModuleConnector> /*here*/, boost::shared_ptr<WModuleConnector> /*there*/ )
397 {
398  // do nothing by default
399 }
400 
401 boost::shared_ptr< WModuleInputConnector > WModuleConnector::toInputConnector()
402 {
403  return boost::dynamic_pointer_cast< WModuleInputConnector >( shared_from_this() );
404 }
405 
406 boost::shared_ptr< WModuleOutputConnector > WModuleConnector::toOutputConnector()
407 {
408  return boost::dynamic_pointer_cast< WModuleOutputConnector >( shared_from_this() );
409 }
410 
t_GenericSignalType signal_ConnectionEstablished
Signal emitted whenever connection has been established.
General purpose exception and therefore base class for all kernel related exceptions.
virtual void disconnectAll()
Disconnects ALL connected connectors.
General purpose exception and therefore base class for all kernel related exceptions.
virtual bool isInputConnector() const =0
Returns true if this instance is an WModuleInputConnector.
General purpose exception and therefore base class for all kernel related exceptions.
const std::string getName() const
Gives name of connection.
boost::weak_ptr< WModule > m_module
The Module this connector belongs to.
static WLogger * getLogger()
Returns pointer to the currently running logger instance.
Definition: WLogger.cpp:64
WModuleConnector(boost::shared_ptr< WModule > module, std::string name="", std::string description="")
Constructor.
std::string m_name
The connections name.
t_GenericSignalType signal_ConnectionClosed
Signal emitted whenever connection has been closed.
virtual void disconnect(boost::shared_ptr< WModuleConnector > con, bool removeFromOwnList=true)
Disconnects this connector if connected.
void addLogMessage(std::string message, std::string source="", LogLevel level=LL_DEBUG)
Appends a log message to the logging queue.
Definition: WLogger.cpp:84
General purpose exception and therefore base class for all kernel related exceptions.
boost::shared_ptr< WModuleInputConnector > toInputConnector()
Tries to convert this instance to an input connector.
virtual void connect(boost::shared_ptr< WModuleConnector > con)
Connects this Module Connector with another one.
virtual bool connectable(boost::shared_ptr< WModuleConnector > con)=0
Checks whether the specified connector is connectable to this one.
unsigned int isConnected()
Gets the count of connections currently established.
General purpose exception and therefore base class for all kernel related exceptions.
std::string m_moduleName
The name of the module owning this connector.
void setDescription(std::string desc)
Sets the connector's description.
const std::string getDescription() const
Gives information about this connection.
void setName(std::string name)
Sets the connector's name.
virtual ~WModuleConnector()
Destructor.
virtual void disconnectSignals(boost::shared_ptr< WModuleConnector > con)
Disconnect all signals subscribed by this connector from "con".
virtual const t_GenericSignalHandlerType getSignalHandler(MODULE_CONNECTOR_SIGNAL signal)
Gives the signal handler function responsible for a given signal.
const std::string getCanonicalName() const
Gives canonical name of connection.
virtual void notifyConnectionClosed(boost::shared_ptr< WModuleConnector > here, boost::shared_ptr< WModuleConnector > there)
Gets called whenever a connection between a remote and local connector gets closed.
Class implementing output connection functionality between modules.
boost::shared_mutex m_connectionListLock
Lock for avoiding concurrent write to m_Connected (multiple reader, single writer lock)...
WCombinerTypes::WOneToOneCombiners getPossibleDisconnections()
Returns a list of possible disconnections for this connector.
virtual void notifyConnectionEstablished(boost::shared_ptr< WModuleConnector > here, boost::shared_ptr< WModuleConnector > there)
Gets called whenever a connector gets connected to the specified input.
virtual void connectSignals(boost::shared_ptr< WModuleConnector > con)
Connect additional signals.
Class implementing input connection functionality between modules.
bool isConnectedTo(boost::shared_ptr< WModuleConnector > con)
Checks whether this connector is connected to the given one.
Combiner which disconnects the specified connection.
boost::shared_ptr< WModuleOutputConnector > toOutputConnector()
Tries to convert this instance to an output connector.
std::set< boost::shared_ptr< WModuleConnector > > m_connected
List of connectors connected to this connector.
Basic exception handler.
Definition: WException.h:38
std::string m_description
The connections description.
Base class for modelling connections between kernel modules.
virtual boost::signals2::connection subscribeSignal(MODULE_CONNECTOR_SIGNAL signal, t_GenericSignalHandlerType notifier)
Connects a specified notify function with a signal this module instance is offering.
General purpose exception and therefore base class for all kernel related exceptions.
boost::shared_ptr< WModule > getModule() const
Returns the module which owns this connector.