OpenWalnut  1.3.1
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