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