The Gaudi Framework  v33r1 (b1225454)
AlgResourcePool.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #include "AlgResourcePool.h"
14 
15 // C++
16 #include <functional>
17 #include <queue>
18 
19 // DP TODO: Manage smartifs and not pointers to algos
20 
21 // Instantiation of a static factory class used by clients to create instances of this service
23 
24 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
25 #define DEBUG_MSG ON_DEBUG debug()
26 
27 //---------------------------------------------------------------------------
28 
29 // destructor
31  for ( auto& algoId_algoQueue : m_algqueue_map ) {
32  auto* queue = algoId_algoQueue.second;
33  delete queue;
34  }
35 }
36 
37 //---------------------------------------------------------------------------
38 
39 // initialize the pool with the list of algos known to the IAlgManager
41 
43  if ( !sc.isSuccess() ) warning() << "Base class could not be started" << endmsg;
44 
45  // Try to recover the topAlgList from the ApplicationManager for backward-compatibility
46  if ( m_topAlgNames.value().empty() ) {
47  info() << "TopAlg list empty. Recovering the one of Application Manager" << endmsg;
48  const Gaudi::Utils::TypeNameString appMgrName( "ApplicationMgr/ApplicationMgr" );
49  SmartIF<IProperty> appMgrProps( serviceLocator()->service( appMgrName ) );
50  m_topAlgNames.assign( appMgrProps->getProperty( "TopAlg" ) );
51  }
52 
53  sc = decodeTopAlgs();
54  if ( sc.isFailure() ) warning() << "Algorithms could not be properly decoded." << endmsg;
55 
56  // let's assume all resources are there
58  return StatusCode::SUCCESS;
59 }
60 
61 //---------------------------------------------------------------------------
62 
64 
65  StatusCode startSc = Service::start();
66  if ( !startSc.isSuccess() ) return startSc;
67 
68  // sys-Start the algos
69  for ( auto& ialgo : m_algList ) {
70  startSc = ialgo->sysStart();
71  if ( startSc.isFailure() ) {
72  error() << "Unable to start Algorithm: " << ialgo->name() << endmsg;
73  return startSc;
74  }
75  }
76  return StatusCode::SUCCESS;
77 }
78 
79 //---------------------------------------------------------------------------
80 
82 
83  std::hash<std::string> hash_function;
84  size_t algo_id = hash_function( name );
85  auto itQueueIAlgPtr = m_algqueue_map.find( algo_id );
86 
87  if ( itQueueIAlgPtr == m_algqueue_map.end() ) {
88  error() << "Algorithm " << name << " requested, but not recognised" << endmsg;
89  algo = nullptr;
90  return StatusCode::FAILURE;
91  }
92 
93  StatusCode sc;
94  if ( blocking ) {
95  itQueueIAlgPtr->second->pop( algo );
96  } else {
97  if ( !itQueueIAlgPtr->second->try_pop( algo ) ) { sc = StatusCode::FAILURE; }
98  }
99 
100  // Note that reentrant algos are not consumed so we put them
101  // back immediately in the queue at the end of this function.
102  // Now we may still be called again in between and get this
103  // error. In such a case, the Scheduler will retry later.
104  // This is of course not optimal, but should only happen very
105  // seldom and thud won't affect the global efficiency
106  if ( sc.isFailure() )
107  DEBUG_MSG << "No instance of algorithm " << name << " could be retrieved in non-blocking mode" << endmsg;
108 
109  // if (m_lazyCreation ) {
110  // TODO: fill the lazyCreation part
111  // }
112  if ( sc.isSuccess() ) {
113  state_type requirements = m_resource_requirements[algo_id];
115  if ( requirements.is_subset_of( m_available_resources ) ) {
116  m_available_resources ^= requirements;
117  } else {
118  sc = StatusCode::FAILURE;
119  error() << "Failure to allocate resources of algorithm " << name << endmsg;
120  // in case of not reentrant, push it back. Reentrant ones are pushed back
121  // in all cases further down
122  if ( 0 != algo->cardinality() ) { itQueueIAlgPtr->second->push( algo ); }
123  }
125  if ( 0 == algo->cardinality() ) {
126  // push back reentrant algos immediately as it can be reused
127  itQueueIAlgPtr->second->push( algo );
128  }
129  }
130  return sc;
131 }
132 
133 //---------------------------------------------------------------------------
134 
136 
137  std::hash<std::string> hash_function;
138  size_t algo_id = hash_function( name );
139 
140  // release resources used by the algorithm
144 
145  // release algorithm itself if not reentrant
146  if ( 0 != algo->cardinality() ) { m_algqueue_map[algo_id]->push( algo ); }
147  return StatusCode::SUCCESS;
148 }
149 
150 //---------------------------------------------------------------------------
151 
156  return StatusCode::SUCCESS;
157 }
158 
159 //---------------------------------------------------------------------------
160 
165  return StatusCode::SUCCESS;
166 }
167 
168 //---------------------------------------------------------------------------
169 
170 StatusCode AlgResourcePool::flattenSequencer( Gaudi::Algorithm* algo, ListAlg& alglist, unsigned int recursionDepth ) {
171 
173 
174  if ( algo->isSequence() ) {
175  auto seq = dynamic_cast<Gaudi::Sequence*>( algo );
176  if ( seq == 0 ) {
177  error() << "Unable to dcast Algorithm " << algo->name() << " to a Sequence, but it has isSequence==true"
178  << endmsg;
179  return StatusCode::FAILURE;
180  }
181 
182  auto subAlgorithms = seq->subAlgorithms();
183 
184  // Recursively unroll
185  ++recursionDepth;
186 
187  for ( auto subalgo : *subAlgorithms ) {
188  sc = flattenSequencer( subalgo, alglist, recursionDepth );
189  if ( sc.isFailure() ) {
190  error() << "Algorithm " << subalgo->name() << " could not be flattened" << endmsg;
191  return sc;
192  }
193  }
194  } else {
195  alglist.emplace_back( algo );
196  return sc;
197  }
198  return sc;
199 }
200 
201 //---------------------------------------------------------------------------
202 
204 
206  if ( !algMan.isValid() ) {
207  error() << "Algorithm manager could not be properly fetched." << endmsg;
208  return StatusCode::FAILURE;
209  }
210 
211  // Useful lambda not to repeat ourselves --------------------------
212  auto createAlg = [&algMan, this]( const std::string& item_type, const std::string& item_name, IAlgorithm*& algo ) {
213  StatusCode createAlgSc = algMan->createAlgorithm( item_type, item_name, algo, true, false );
214  if ( createAlgSc.isFailure() )
215  this->warning() << "Algorithm " << item_type << "/" << item_name << " could not be created." << endmsg;
216  };
217  // End of lambda --------------------------------------------------
218 
220 
221  // Fill the top alg list ----
222  const std::vector<std::string>& topAlgNames = m_topAlgNames.value();
223  for ( auto& name : topAlgNames ) {
224  IAlgorithm* algo( nullptr );
225 
227  const std::string& item_name = item.name();
228  const std::string& item_type = item.type();
229  SmartIF<IAlgorithm> algoSmartIF( algMan->algorithm( item_name, false ) );
230 
231  if ( !algoSmartIF.isValid() ) {
232  createAlg( item_type, item_name, algo );
233  algoSmartIF = algo;
234  }
235  // Init and start
236  algoSmartIF->sysInitialize().ignore();
237  m_topAlgList.push_back( algoSmartIF );
238  }
239  // Top Alg list filled ----
240 
241  // Now we unroll it ----
242  for ( auto& algoSmartIF : m_topAlgList ) {
243  Gaudi::Algorithm* algorithm = dynamic_cast<Gaudi::Algorithm*>( algoSmartIF.get() );
244  if ( !algorithm ) {
245  fatal() << "Conversion from IAlgorithm to Gaudi::Algorithm failed" << endmsg;
246  return StatusCode::FAILURE;
247  }
248  sc = flattenSequencer( algorithm, m_flatUniqueAlgList );
249  }
250  // stupid O(N^2) unique-ification..
251  for ( auto i = begin( m_flatUniqueAlgList ); i != end( m_flatUniqueAlgList ); ++i ) {
252  auto n = next( i );
253  while ( n != end( m_flatUniqueAlgList ) ) {
254  if ( *n == *i )
256  else
257  ++n;
258  }
259  }
260  if ( msgLevel( MSG::DEBUG ) ) {
261  debug() << "List of algorithms is: " << endmsg;
262  for ( auto& algo : m_flatUniqueAlgList )
263  debug() << " o " << algo->type() << "/" << algo->name() << " @ " << algo << endmsg;
264  }
265 
266  // Unrolled ---
267 
268  // Now let's manage the clones
269  unsigned int resource_counter( 0 );
270  std::hash<std::string> hash_function;
271  for ( auto& ialgoSmartIF : m_flatUniqueAlgList ) {
272 
273  const std::string& item_name = ialgoSmartIF->name();
274 
275  verbose() << "Treating resource management and clones of " << item_name << endmsg;
276 
277  Gaudi::Algorithm* algo = dynamic_cast<Gaudi::Algorithm*>( ialgoSmartIF.get() );
278  if ( !algo ) {
279  fatal() << "Conversion from IAlgorithm to Gaudi::Algorithm failed" << endmsg;
280  return StatusCode::FAILURE;
281  }
282  const std::string& item_type = algo->type();
283 
284  size_t algo_id = hash_function( item_name );
286  m_algqueue_map[algo_id] = queue;
287 
288  // DP TODO Do it properly with SmartIFs, also in the queues
289  IAlgorithm* ialgo( ialgoSmartIF.get() );
290 
291  queue->push( ialgo );
292  m_algList.push_back( ialgo );
293  if ( ialgo->isReEntrant() ) {
294  if ( ialgo->cardinality() != 0 ) {
295  info() << "Algorithm " << ialgo->name() << " is ReEntrant, but Cardinality was set to " << ialgo->cardinality()
296  << endmsg;
297  m_n_of_allowed_instances[algo_id] = ialgo->cardinality();
298  } else {
299  m_n_of_allowed_instances[algo_id] = 1;
300  }
301  } else if ( ialgo->isClonable() ) {
302  m_n_of_allowed_instances[algo_id] = ialgo->cardinality();
303  } else {
304  if ( ialgo->cardinality() == 1 ) {
305  m_n_of_allowed_instances[algo_id] = 1;
306  } else {
307  if ( !m_overrideUnClonable ) {
308  info() << "Algorithm " << ialgo->name() << " is un-Clonable but Cardinality was set to "
309  << ialgo->cardinality() << ". Only creating 1 instance" << endmsg;
310  m_n_of_allowed_instances[algo_id] = 1;
311  } else {
312  warning() << "Overriding UnClonability of Algorithm " << ialgo->name() << ". Setting Cardinality to "
313  << ialgo->cardinality() << endmsg;
314  m_n_of_allowed_instances[algo_id] = ialgo->cardinality();
315  }
316  }
317  }
318  m_n_of_created_instances[algo_id] = 1;
319 
320  state_type requirements( 0 );
321 
322  for ( auto& resource_name : ialgo->neededResources() ) {
323  auto ret = m_resource_indices.emplace( resource_name, resource_counter );
324  // insert successful means == wasn't known before. So increment counter
325  if ( ret.second ) ++resource_counter;
326  // Resize for every algo according to the found resources
327  requirements.resize( resource_counter );
328  // in any case the return value holds the proper product index
329  requirements[ret.first->second] = true;
330  }
331 
332  m_resource_requirements[algo_id] = requirements;
333 
334  // potentially create clones; if not lazy creation we have to do it now
335  if ( !m_lazyCreation ) {
336  for ( unsigned int i = 1, end = m_n_of_allowed_instances[algo_id]; i < end; ++i ) {
337  debug() << "type/name to create clone of: " << item_type << "/" << item_name << endmsg;
338  IAlgorithm* ialgoClone( nullptr );
339  createAlg( item_type, item_name, ialgoClone );
340  ialgoClone->setIndex( i );
341  if ( ialgoClone->sysInitialize().isFailure() ) {
342  error() << "unable to initialize Algorithm clone " << ialgoClone->name() << endmsg;
343  sc = StatusCode::FAILURE;
344  // FIXME: should we delete this failed clone?
345  } else {
346  queue->push( ialgoClone );
347  m_n_of_created_instances[algo_id] += 1;
348  }
349  }
350  }
351  }
352 
353  // Now resize all the requirement bitsets to the same size
354  for ( auto& kv : m_resource_requirements ) { kv.second.resize( resource_counter ); }
355 
356  // Set all resources to be available
357  m_available_resources.resize( resource_counter );
358  m_available_resources.set();
359 
360  return sc;
361 }
362 
363 //---------------------------------------------------------------------------
364 
367  for ( auto algoSmartIF : m_flatUniqueAlgList ) m_flatUniqueAlgPtrList.push_back( algoSmartIF.get() );
368  return m_flatUniqueAlgPtrList;
369 }
370 
371 //---------------------------------------------------------------------------
372 
375  for ( auto algoSmartIF : m_topAlgList ) m_topAlgPtrList.push_back( algoSmartIF.get() );
376  return m_topAlgPtrList;
377 }
378 
379 //---------------------------------------------------------------------------
380 
382 
383  StatusCode stopSc = Service::stop();
384  if ( !stopSc.isSuccess() ) return stopSc;
385 
386  // sys-Stop the algos
387  for ( auto& ialgo : m_algList ) {
388  stopSc = ialgo->sysStop();
389  if ( stopSc.isFailure() ) {
390  error() << "Unable to stop Algorithm: " << ialgo->name() << endmsg;
391  return stopSc;
392  }
393  }
394  return StatusCode::SUCCESS;
395 }
ListAlg m_flatUniqueAlgList
The flat list of algorithms w/o clones.
virtual SmartIF< IAlgorithm > & algorithm(const Gaudi::Utils::TypeNameString &typeName, const bool createIf=true)=0
Returns a smart pointer to a service.
StatusCode initialize() override
Definition: Service.cpp:70
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition: Service.cpp:287
T unlock(T... args)
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
StatusCode start() override
Definition: Service.cpp:139
std::map< std::string, unsigned int > m_resource_indices
bool isValid() const
Allow for check if smart pointer is valid.
Definition: SmartIF.h:72
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
std::mutex m_resource_mutex
const std::string & type() const
StatusCode stop() override
T end(T... args)
virtual unsigned int cardinality() const =0
Cardinality (Maximum number of clones that can exist) special value 0 means that algorithm is reentra...
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
The AlgResourcePool is a concrete implementation of the IAlgResourcePool interface.
state_type m_available_resources
Gaudi::Property< bool > m_overrideUnClonable
virtual StatusCode sysInitialize()=0
Initialization method invoked by the framework.
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
STL class.
#define DECLARE_COMPONENT(type)
boost::dynamic_bitset state_type
const std::string & name() const override
Retrieve name of the service.
Definition: Service.cpp:284
T push_back(T... args)
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
Helper class to parse a string of format "type/name".
tbb::concurrent_bounded_queue< IAlgorithm * > concurrentQueueIAlgPtr
std::list< IAlgorithm * > m_flatUniqueAlgPtrList
The flat list of algorithms w/o clones which is returned.
const std::string & name() const
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:61
const std::string & type() const override
The type of the algorithm object.
Definition: Algorithm.h:166
std::map< size_t, state_type > m_resource_requirements
T erase(T... args)
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
StatusCode start() override
virtual StatusCode getProperty(Gaudi::Details::PropertyBase *p) const =0
Get the property by property.
T lock(T... args)
~AlgResourcePool() override
std::list< IAlgorithm * > m_topAlgPtrList
The top list of algorithms.
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
virtual void setIndex(const unsigned int &idx)=0
Set instantiation index of Alg.
std::list< IAlgorithm * > getFlatAlgList() override
def end
Definition: IOTest.py:123
StatusCode releaseResource(const std::string &name) override
Release a certrain resource.
Gaudi::Property< std::vector< std::string > > m_topAlgNames
std::list< IAlgorithm * > getTopAlgList() override
StatusCode stop() override
Definition: Service.cpp:133
T clear(T... args)
bool isSuccess() const
Definition: StatusCode.h:365
The IAlgorithm is the interface implemented by the Algorithm base class.
Definition: IAlgorithm.h:38
StatusCode acquireResource(const std::string &name) override
Acquire a certain resource.
std::map< size_t, concurrentQueueIAlgPtr * > m_algqueue_map
T find(T... args)
const StatusCode & ignore() const
Ignore/check StatusCode.
Definition: StatusCode.h:168
StatusCode decodeTopAlgs()
Decode the top alg list.
StatusCode flattenSequencer(Gaudi::Algorithm *sequencer, ListAlg &alglist, unsigned int recursionDepth=0)
Recursively flatten an algList.
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:89
T emplace(T... args)
StatusCode acquireAlgorithm(const std::string &name, IAlgorithm *&algo, bool blocking=false) override
Acquire a certain algorithm using its name.
constexpr static const auto FAILURE
Definition: StatusCode.h:101
bool isSequence() const override
Are we a Sequence?
Definition: Algorithm.h:199
virtual StatusCode createAlgorithm(std::string algtype, std::string algname, IAlgorithm *&alg, bool managed=false, bool checkIfExists=true)=0
Create an instance of a algorithm type that has been declared beforehand and assigns to it a name.
StatusCode service(const std::string &name, const T *&psvc, bool createIf=true) const
Access a service by name, creating it if it doesn't already exist.
Definition: Service.h:93
std::map< size_t, unsigned int > m_n_of_created_instances
bool isFailure() const
Definition: StatusCode.h:145
#define DEBUG_MSG
AttribStringParser::Iterator begin(const AttribStringParser &parser)
StatusCode initialize() override
ListAlg m_topAlgList
The list of top algorithms.
std::map< size_t, size_t > m_n_of_allowed_instances
MsgStream & fatal() const
shortcut for the method msgStream(MSG::FATAL)
ListAlg m_algList
The list of all algorithms created withing the Pool which are not top.
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:202
StatusCode releaseAlgorithm(const std::string &name, IAlgorithm *&algo) override
Release a certain algorithm.
const std::string & name() const override
The identifying name of the algorithm object.
Definition: Algorithm.cpp:549
Gaudi::Property< bool > m_lazyCreation
T emplace_back(T... args)