OpenWalnut  1.2.5
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
WModule.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 #ifdef __linux__
26 #include <sys/prctl.h>
27 #endif
28 
29 #include <algorithm>
30 #include <set>
31 #include <string>
32 #include <sstream>
33 
34 #include <boost/shared_ptr.hpp>
35 
36 #include "WModuleInputConnector.h"
37 #include "WModuleOutputConnector.h"
38 #include "WModuleInputData.h"
39 #include "WModuleOutputData.h"
40 #include "WModuleConnectorSignals.h"
41 #include "WModuleContainer.h"
42 #include "WModuleFactory.h"
43 #include "exceptions/WModuleSignalUnknown.h"
44 #include "exceptions/WModuleSignalSubscriptionFailed.h"
45 #include "exceptions/WModuleConnectorInitFailed.h"
46 #include "exceptions/WModuleConnectorNotFound.h"
47 #include "exceptions/WModuleUninitialized.h"
48 #include "exceptions/WModuleRequirementNotMet.h"
49 #include "../common/WException.h"
50 #include "../common/exceptions/WNameNotUnique.h"
51 #include "../common/WLogger.h"
52 #include "../common/WCondition.h"
53 #include "../common/WConditionOneShot.h"
54 #include "../common/WConditionSet.h"
55 #include "../common/WPathHelper.h"
56 #include "../common/WProgressCombiner.h"
57 #include "../common/WPredicateHelper.h"
58 
59 #include "WModule.h"
60 
63  WPrototyped(),
64  m_initialized( new WCondition(), false ),
65  m_isAssociated( new WCondition(), false ),
66  m_isUsable( new WCondition(), false ),
67  m_isReady( new WConditionOneShot(), false ),
68  m_isCrashed( new WConditionOneShot(), false ),
69  m_isReadyOrCrashed( new WConditionSet(), false ),
70  m_isRunning( new WCondition(), false ),
71  m_readyProgress( boost::shared_ptr< WProgress >( new WProgress( "Initializing Module" ) ) ),
72  m_moduleState(),
73  m_localPath( WPathHelper::getSharePath() )
74 {
75  // initialize members
76  m_properties = boost::shared_ptr< WProperties >( new WProperties( "Properties", "Module's properties" ) );
77  m_infoProperties = boost::shared_ptr< WProperties >( new WProperties( "Informational Properties", "Module's information properties" ) );
78  m_infoProperties->setPurpose( PV_PURPOSE_INFORMATION );
79 
80  m_runtimeName = m_properties->addProperty( "Name", "The name of the module defined by the user. This is, by default, the module name but "
81  "can be changed by the user to provide some kind of simple identification upon many modules.",
82  std::string( "" ), false );
83 
84  m_active = m_properties->addProperty( "active", "Determines whether the module should be activated.", true, true );
85  m_active->getCondition()->subscribeSignal( boost::bind( &WModule::activate, this ) );
86 
87  // the isReadyOrCrashed condition set needs to be set up here
88  WConditionSet* cs = static_cast< WConditionSet* >( m_isReadyOrCrashed.getCondition().get() ); // NOLINT
89  cs->setResetable( true, false );
90  cs->add( m_isReady.getCondition() );
91  cs->add( m_isCrashed.getCondition() );
92 
93  m_container = boost::shared_ptr< WModuleContainer >();
94  m_progress = boost::shared_ptr< WProgressCombiner >( new WProgressCombiner() );
95 
96  // add a progress indicator which finishes on "ready()"
97  m_progress->addSubProgress( m_readyProgress );
98 
99  // our internal state consist out of two conditions: data changed and the exit flag from WThreadedRunner.
101 }
102 
104 {
105  // cleanup
106 }
107 
108 void WModule::addConnector( boost::shared_ptr< WModuleInputConnector > con )
109 {
110  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
112  );
113  // well ... we want it to be unique in both:
114  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
116  );
117 
118  // if there already is one ... exception
119  if( c )
120  {
121  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
122  }
123 
124  m_inputConnectors.push_back( con );
125 }
126 
127 void WModule::addConnector( boost::shared_ptr< WModuleOutputConnector > con )
128 {
129  size_t c = std::count_if( m_inputConnectors.begin(), m_inputConnectors.end(),
131  );
132  // well ... we want it to be unique in both:
133  c += std::count_if( m_outputConnectors.begin(), m_outputConnectors.end(),
135  );
136 
137  // if there already is one ... exception
138  if( c )
139  {
140  throw WNameNotUnique( std::string( "Could not add the connector " + con->getCanonicalName() + " since names must be unique." ) );
141  }
142 
143  m_outputConnectors.push_back( con );
144 }
145 
147 {
148  // remove connections and their signals
149  for( InputConnectorList::iterator listIter = m_inputConnectors.begin();
150  listIter != m_inputConnectors.end(); ++listIter )
151  {
152  ( *listIter )->disconnectAll();
153  }
154  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin();
155  listIter != m_outputConnectors.end(); ++listIter )
156  {
157  ( *listIter )->disconnectAll();
158  }
159 }
160 
161 WCombinerTypes::WDisconnectList WModule::getPossibleDisconnections()
162 {
163  WCombinerTypes::WDisconnectList discons;
164 
165  // iterate inputs
166  for( InputConnectorList::iterator listIter = m_inputConnectors.begin(); listIter != m_inputConnectors.end(); ++listIter )
167  {
168  // get all connections of the current connector:
169  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
170  ( *listIter )->getPossibleDisconnections() );
171 
172  if( g.second.size() )
173  {
174  discons.push_back( g );
175  }
176  }
177 
178  // iterate outputs
179  for( OutputConnectorList::iterator listIter = m_outputConnectors.begin(); listIter != m_outputConnectors.end(); ++listIter )
180  {
181  // get all connections of the current connector:
182  WCombinerTypes::WDisconnectGroup g = WCombinerTypes::WDisconnectGroup( ( *listIter )->getName(),
183  ( *listIter )->getPossibleDisconnections() );
184 
185  if( g.second.size() )
186  {
187  discons.push_back( g );
188  }
189  }
190 
191  return discons;
192 }
193 
195 {
196  m_initialized( false );
198 
199  // remove connections and their signals, this is flat removal. The module container can do deep removal
200  disconnect();
201 
202  // clean up list
203  // this should delete the connector since nobody else *should* have another shared_ptr to them
204  m_inputConnectors.clear();
205  m_outputConnectors.clear();
206 }
207 
209 {
210 }
211 
213 {
214 }
215 
217 {
218 }
219 
221 {
222 }
223 
224 std::string WModule::deprecated() const
225 {
226  return "";
227 }
228 
230 {
231  // doing it twice is not allowed
232  if( isInitialized()() )
233  {
234  throw WModuleConnectorInitFailed( std::string( "Could not initialize connectors for Module " ) + getName() +
235  std::string( ". Reason: already initialized." ) );
236  }
237 
238  // set the module name as default runtime name
239  m_runtimeName->set( getName() );
240 
241  // initialize connectors and properties
242  requirements();
243  connectors();
244  properties();
245 
246  // now, the module is initialized but not necessarily usable (if not associated with a container)
247  m_initialized( true );
249 }
250 
252 {
253  // currently just removes connectors
255 }
256 
257 boost::shared_ptr< WModuleContainer > WModule::getAssociatedContainer() const
258 {
259  return m_container;
260 }
261 
262 void WModule::setAssociatedContainer( boost::shared_ptr< WModuleContainer > container )
263 {
264  m_container = container;
265 
266  // true if the pointer is set
267  m_isAssociated( m_container != boost::shared_ptr< WModuleContainer >() );
269 }
270 
271 MODULE_TYPE WModule::getType() const
272 {
273  return MODULE_ARBITRARY;
274 }
275 
277 {
278  return m_inputConnectors;
279 }
280 
282 {
283  return m_outputConnectors;
284 }
285 
286 boost::shared_ptr< WModuleInputConnector > WModule::findInputConnector( std::string name )
287 {
288  // simply search
289  for( InputConnectorList::const_iterator listIter = m_inputConnectors.begin();
290  listIter != m_inputConnectors.end(); ++listIter )
291  {
292  // try the canonical name
293  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
294  {
295  return ( *listIter );
296  }
297  }
298 
299  return boost::shared_ptr< WModuleInputConnector >();
300 }
301 
302 boost::shared_ptr< WModuleInputConnector > WModule::getInputConnector( std::string name )
303 {
304  boost::shared_ptr< WModuleInputConnector > p = findInputConnector( name );
305 
306  if( !p )
307  {
308  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
309  std::string( "\" does not exist in the module \"" ) + getName() + std::string( "\"." ) );
310  }
311 
312  return p;
313 }
314 
315 boost::shared_ptr< WModuleOutputConnector > WModule::findOutputConnector( std::string name )
316 {
317  // simply search
318  for( OutputConnectorList::const_iterator listIter = m_outputConnectors.begin();
319  listIter != m_outputConnectors.end(); ++listIter )
320  {
321  // try the canonical name
322  if( ( name == ( *listIter )->getCanonicalName() ) || ( name == ( *listIter )->getName() ) )
323  {
324  return ( *listIter );
325  }
326  }
327 
328  return boost::shared_ptr< WModuleOutputConnector >();
329 }
330 
331 boost::shared_ptr< WModuleOutputConnector > WModule::getOutputConnector( std::string name )
332 {
333  boost::shared_ptr< WModuleOutputConnector > p = findOutputConnector( name );
334 
335  if( !p )
336  {
337  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
338  std::string( "\" does not exist in the module \"" ) + getName() +
339  std::string( "\"." ) );
340  }
341 
342  return p;
343 }
344 
345 boost::shared_ptr< WModuleConnector > WModule::findConnector( std::string name )
346 {
347  // simply search both
348  boost::shared_ptr< WModuleConnector > p = findInputConnector( name );
349  if( p ) // found?
350  {
351  return p;
352  }
353 
354  // search in output list
355  return findOutputConnector( name );
356 }
357 
358 boost::shared_ptr< WModuleConnector > WModule::getConnector( std::string name )
359 {
360  boost::shared_ptr< WModuleConnector > p = findConnector( name );
361 
362  if( !p )
363  {
364  throw WModuleConnectorNotFound( std::string( "The connector \"" ) + name +
365  std::string( "\" does not exist in the module \"" ) + getName() +
366  std::string( "\"." ) );
367  }
368 
369  return p;
370 }
371 
372 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleGenericSignalHandlerType notifier )
373 {
374  switch ( signal )
375  {
376  case WM_READY:
377  return signal_ready.connect( notifier );
378  default:
379  std::ostringstream s;
380  s << "Could not subscribe to unknown signal.";
381  throw WModuleSignalSubscriptionFailed( s.str() );
382  break;
383  }
384 }
385 
386 boost::signals2::connection WModule::subscribeSignal( MODULE_SIGNAL signal, t_ModuleErrorSignalHandlerType notifier )
387 {
388  switch (signal)
389  {
390  case WM_ERROR:
391  return signal_error.connect( notifier );
392  default:
393  std::ostringstream s;
394  s << "Could not subscribe to unknown signal.";
395  throw WModuleSignalSubscriptionFailed( s.str() );
396  break;
397  }
398 }
399 
400 const t_GenericSignalHandlerType WModule::getSignalHandler( MODULE_CONNECTOR_SIGNAL signal )
401 {
402  switch ( signal )
403  {
404  case CONNECTION_ESTABLISHED:
405  return boost::bind( &WModule::notifyConnectionEstablished, this, _1, _2 );
406  case CONNECTION_CLOSED:
407  return boost::bind( &WModule::notifyConnectionClosed, this, _1, _2 );
408  case DATA_CHANGED:
409  return boost::bind( &WModule::notifyDataChange, this, _1, _2 );
410  default:
411  std::ostringstream s;
412  s << "Could not subscribe to unknown signal. You need to implement this signal type explicitly in your module.";
413  throw WModuleSignalUnknown( s.str() );
414  break;
415  }
416 }
417 
419 {
420  return m_initialized;
421 }
422 
424 {
425  return m_isAssociated;
426 }
427 
429 {
430  return m_isUsable;
431  //return isInitialized() && isAssociated();
432 }
433 
435 {
436  return m_isReady;
437 }
438 
440 {
441  return m_isCrashed;
442 }
443 
445 {
446  return m_isReadyOrCrashed;
447 }
448 
450 {
451  return m_isRunning;
452 }
453 
454 void WModule::notifyConnectionEstablished( boost::shared_ptr< WModuleConnector > /*here*/,
455  boost::shared_ptr< WModuleConnector > /*there*/ )
456 {
457  // By default this callback does nothing. Overwrite it in your module.
458 }
459 
460 void WModule::notifyConnectionClosed( boost::shared_ptr< WModuleConnector > /*here*/,
461  boost::shared_ptr< WModuleConnector > /*there*/ )
462 {
463  // By default this callback does nothing. Overwrite it in your module.
464 }
465 
466 void WModule::notifyDataChange( boost::shared_ptr< WModuleConnector > /*input*/,
467  boost::shared_ptr< WModuleConnector > /*output*/ )
468 {
469  // By default this callback does nothing. Overwrite it in your module.
470 }
471 
472 boost::shared_ptr< WProperties > WModule::getProperties() const
473 {
474  return m_properties;
475 }
476 
477 boost::shared_ptr< WProperties > WModule::getInformationProperties() const
478 {
479  return m_infoProperties;
480 }
481 
482 boost::shared_ptr< WProgressCombiner > WModule::getRootProgressCombiner()
483 {
484  return m_progress;
485 }
486 
487 const char** WModule::getXPMIcon() const
488 {
489  // return empty 1x1 icon by default.
490  static const char * o_xpm[] =
491  {
492  "1 1 1 1",
493  " c None",
494  " "
495  };
496  return o_xpm;
497 }
498 
500 {
501  m_isReady( true );
502  m_readyProgress->finish();
503  signal_ready( shared_from_this() );
504 }
505 
507 {
508  // simply iterate all requirements and return the first found that is not fulfilled
509  for( Requirements::const_iterator i = m_requirements.begin(); i != m_requirements.end(); ++i )
510  {
511  if( !( *i )->isComplied() )
512  {
513  return *i;
514  }
515  }
516 
517  return NULL;
518 }
519 
521 {
522 #ifdef __linux__
523  // set the name of the thread. This name is shown by the "top", for example.
524  prctl( PR_SET_NAME, ( "openwalnut (" + getName() + ")" ).c_str() );
525 #endif
526 
527  try
528  {
529  WLogger::getLogger()->addLogMessage( "Starting module main method.", "Module (" + getName() + ")", LL_INFO );
530 
531  // check requirements
532  const WRequirement* failedReq = checkRequirements();
533  if( failedReq )
534  {
535  throw WModuleRequirementNotMet( failedReq );
536  }
537 
538  // call main thread function
539  m_isRunning( true );
540  moduleMain();
541  }
542  catch( const WException& e )
543  {
544  wlog::error( "Module (" + getName() +")" ) << "WException. Notifying. Message: " << e.what();
545 
546  // ensure proper exception propagation
547  signal_error( shared_from_this(), e );
548 
549  // hopefully, all waiting threads use isReadyOrCrashed to wait.
550  m_isCrashed( true );
551  }
552  catch( const std::exception& e )
553  {
554  // convert these exceptions to WException
555  WException ce = WException( e );
556 
557  // print this message AFTER creation of WException to have the backtrace before the message
558  WLogger::getLogger()->addLogMessage( std::string( "Exception. Notifying. Message: " ) + e.what(), "Module (" + getName() + ")", LL_ERROR );
559 
560  // communicate error
561  signal_error( shared_from_this(), ce );
562 
563  // hopefully, all waiting threads use isReadyOrCrashed to wait.
564  m_isCrashed( true );
565  }
566 
567  // remove all pending connections. This is important as connections that still exists after module deletion can cause segfaults when they get
568  // disconnected in the connector destructor.
569  disconnect();
570  m_isRunning( false );
571 }
572 
574 {
575  return wlog::info( getName() );
576 }
577 
579 {
580  return wlog::error( getName() );
581 }
582 
584 {
585  return wlog::debug( getName() );
586 }
587 
589 {
590  return wlog::warn( getName() );
591 }
592 
593 void WModule::setLocalPath( boost::filesystem::path path )
594 {
595  m_localPath = path;
596 }
597 
598 boost::filesystem::path WModule::getLocalPath() const
599 {
600  return m_localPath;
601 }
602 
604 {
605  return !deprecated().empty();
606 }
607 
609 {
610  return deprecated();
611 }
612