The Gaudi Framework  v30r4 (9b837755)
DataHandleHolderBase.h
Go to the documentation of this file.
1 #ifndef GAUDIKERNEL_DATAHANDLEHOLDERBASE
2 #define GAUDIKERNEL_DATAHANDLEHOLDERBASE 1
3 
4 #include <algorithm>
5 #include <type_traits>
6 #include <unordered_set>
7 #include <vector>
8 
9 #include <boost/optional.hpp>
10 
11 #include "GaudiKernel/DataHandle.h"
12 #include "GaudiKernel/DataObjID.h"
15 #include "GaudiKernel/IStateful.h"
16 #include "GaudiKernel/Property.h"
18 
26 template <typename BASE>
27 class GAUDI_API DataHandleHolderBase : public extends<BASE, IDataHandleHolder>
28 {
31 
32 public:
34  template <typename... Args>
35  DataHandleHolderBase( Args&&... args ) : Super( std::forward<Args>( args )... )
36  {
37  }
38 
44  void registerDataHandle( Gaudi::v2::DataHandle& handle ) final override
45  {
46  if ( m_explicitDepsCollected && isNewDataDependency( handle ) ) {
47  throw GaudiException( "Cannot register a new data handle after data dependency collection", this->name(),
49  }
50  auto index = accessToIndex( handle.access() );
51  m_dataHandles[index].push_back( &handle );
52  }
53 
62  void addDataDependency( const DataObjID& key, AccessMode access ) final override
63  {
64  auto index = accessToIndex( access );
65  m_dataDepKeys[index].insert( key );
66  }
67 
73  const DataObjIDColl& dataDependencies( AccessMode access ) const final override
74  {
75  auto index = accessToIndex( access );
76  return allDataDependencies()[index];
77  }
78 
80  void declare( Gaudi::v1::DataHandle& handle ) override
81  {
82  if ( m_explicitDepsCollected && isNewDataDependency( handle ) ) {
83  throw GaudiException( "Cannot register a new data handle after data dependency collection", this->name(),
85  }
86 
87  if ( !handle.owner() ) {
88  handle.setOwner( this );
89  } else if ( handle.owner() != this ) {
90  throw GaudiException( "Attempt to declare foreign handle with algorithm!", this->name(), StatusCode::FAILURE );
91  }
92 
93  m_legacyHandles.insert( &handle );
94  }
95 
97  void renounce( Gaudi::v1::DataHandle& handle ) override
98  {
99  // In general, deleting a DataHandle after the data dependencies have been
100  // collected is bogus, because this change will not propagate to the data
101  // dependency list, and to our clients that have queried and potentially
102  // made copies of this list, such as the Scheduler.
103  //
104  // Nevertheless, this operation is safe and should be allowed during
105  // and after Algorithm finalization.
106  //
107  if ( m_explicitDepsCollected &&
108  dynamic_cast<IStateful*>( this )->targetFSMState() >= Gaudi::StateMachine::INITIALIZED ) {
109  throw GaudiException( "Cannot discard legacy handle after data dependency collection", this->name(),
111  }
112 
113  if ( handle.owner() != this ) {
114  throw GaudiException( "Attempt to renounce foreign handle with algorithm!", this->name(), StatusCode::FAILURE );
115  }
116 
117  m_legacyHandles.erase( &handle );
118  }
119 
120  // Access the internal array of data dependencies. This is only meant for
121  // internal use, but must be public as we access it via IDataHandleHolder.
122  const DataObjIDColl* allDataDependencies() const final override
123  {
124  if ( !m_explicitDepsCollected ) {
125  throw GaudiException( "Cannot read data dependencies before they are collected", this->name(),
127  }
128  return m_dataDepKeys;
129  }
130 
131 protected:
142 
157  {
158  updateDependencies( m_dataHandles, keyMap );
159  updateDependencies( m_legacyHandles, keyMap );
160  updateDependencies( m_dataDepKeys, keyMap );
161  updateDependencies( m_extraDeps, keyMap );
162  }
163 
174  {
175  // Considering this function's interface contract, calling it multiple
176  // times is very likely to be the result of a usage error.
177  if ( m_explicitDepsCollected ) {
178  throw GaudiException( "Explicit dependencies may only be collected once", this->name(), StatusCode::FAILURE );
179  }
180 
181  // Collect the centralized lists of inputs and outputs
182  collectDependencies( m_dataHandles );
183  collectDependencies( m_extraDeps );
184  collectDependencies( m_legacyHandles );
185 
186  // Take note that the explicit dependencies have been collected
187  m_explicitDepsCollected = true;
188  }
189 
208  enum class CircularDepAction {
211  Abort,
212 
216  Ignore
217  };
218 
225 
233  {
234  // Make sure that explicit dependencies have been collected beforehand
235  if ( !m_explicitDepsCollected ) {
236  throw GaudiException( "Explicit dependencies must be collected first", this->name(), StatusCode::FAILURE );
237  }
238 
239  // Go through the output dependencies, looking for circular input
240  // dependencies
241  //
242  // We must use this inefficient O(N²) iteration pattern because circular
243  // dependency handling may mutate the input dependency list,
244  // invalidating any iterator to that list.
245  //
246  const DataObjIDColl& outputKeys = m_dataDepKeys[accessToIndex( AccessMode::Write )];
247  DataObjIDColl& inputKeys = m_dataDepKeys[accessToIndex( AccessMode::Read )];
248  for ( const auto& outputKey : outputKeys ) {
249  auto inputPos = inputKeys.find( outputKey );
250  if ( inputPos != inputKeys.end() ) {
251  // Call the user circular dependency handler on each dependency
252  switch ( circularDepHandler( *inputPos ) ) {
253  // Abort iteration if asked to do so
254  case CircularDepAction::Abort:
255  return StatusCode::FAILURE;
256 
257  // Break the circular dependency otherwise
258  case CircularDepAction::Ignore:
259  inputKeys.erase( inputPos );
260  }
261  }
262  }
263 
264  // If control reached this point, we are fine
265  return StatusCode::SUCCESS;
266  }
267 
280  {
281  if ( !child ) return; // Framework randomly sends nulls to this function...
282  collectDependencies( child->allDataDependencies() );
283  }
284 
290  {
291  if ( !m_explicitDepsCollected ) {
292  throw GaudiException( "Cannot read ignored data dependencies before they are collected", this->name(),
294  }
295  return m_ignoredDataDeps[accessToIndex( access )];
296  }
297 
304  {
305  for ( auto handle : m_legacyHandles ) handle->init();
306 
307  for ( auto handleArray : m_dataHandles ) {
308  for ( auto handle : handleArray ) {
309  handle->initialize( *this );
310  }
311  }
312  }
313 
314 private:
315  // We need to know the number of DataHandle access modes
316  static constexpr size_t NUM_ACCESS_MODES = static_cast<size_t>( AccessMode::NUM_ACCESS_MODES );
317 
318  // Data handles associated with input and output data
320  DataHandleList m_dataHandles[NUM_ACCESS_MODES];
321 
322  // Legacy DataHandles
324 
325  // Properties allowing extra input and output dependencies to be
326  // declared at configuration time.
327  static_assert( NUM_ACCESS_MODES == 2,
328  "This part of the code assumes that there are only two DataHandle access modes" );
329  static_assert( static_cast<size_t>( AccessMode::Read ) == 0,
330  "This part of the code assumes that \"Read\" comes first in the AccessMode enum" );
331  Gaudi::Property<DataObjIDColl> m_extraDeps[NUM_ACCESS_MODES]{{this, "ExtraInputs", DataObjIDColl{}},
332  {this, "ExtraOutputs", DataObjIDColl{}}};
333 
334  // Location where all data dependencies will be eventually collected
335  DataObjIDColl m_dataDepKeys[NUM_ACCESS_MODES];
336 
339  bool m_explicitDepsCollected = false;
340 
341  // Places where we keep track of the inputs and outputs that were
342  // ignored because they had an empty key
343  DataObjIDColl m_ignoredDataDeps[NUM_ACCESS_MODES];
344 
346  static size_t accessToIndex( AccessMode access ) { return static_cast<size_t>( access ); }
347 
349  bool isNewDataDependency( const Gaudi::v2::DataHandle& handle ) const
350  {
351  auto index = accessToIndex( handle.access() );
352  return m_dataDepKeys[index].find( handle.targetKey() ) == m_dataDepKeys[index].end();
353  }
354 
356  bool isNewDataDependency( const Gaudi::v1::DataHandle& handle ) const
357  {
358  bool result = false;
359  if ( handle.mode() & Gaudi::v1::DataHandle::Reader ) {
360  auto index = accessToIndex( AccessMode::Read );
361  result &= ( m_dataDepKeys[index].find( handle.fullKey() ) == m_dataDepKeys[index].end() );
362  }
363  if ( handle.mode() & Gaudi::v1::DataHandle::Writer ) {
364  auto index = accessToIndex( AccessMode::Write );
365  result &= ( m_dataDepKeys[index].find( handle.fullKey() ) == m_dataDepKeys[index].end() );
366  }
367  return result;
368  }
369 
371  static const DataObjID& accessKey( const Gaudi::v2::DataHandle* handlePtr ) { return handlePtr->targetKey(); }
372 
374  static const DataObjID& accessKey( const DataObjID& key ) { return key; }
375 
377  static const DataObjID& accessKey( const Gaudi::v1::DataHandle* handlePtr ) { return handlePtr->fullKey(); }
378 
380  template <typename KeyHolder>
381  static void extractKey( DataObjIDColl& validOutput, DataObjIDColl& ignoredOutput, const KeyHolder& holder )
382  {
383  const DataObjID& key = accessKey( holder );
384  if ( !key.empty() ) {
385  validOutput.insert( key );
386  } else {
387  ignoredOutput.insert( key );
388  }
389  }
390 
398  template <typename KeyHolderColl>
399  static void extractKeys( DataObjIDColl& validOutput, DataObjIDColl& ignoredOutput, const KeyHolderColl& keyHolders )
400  {
401  validOutput.reserve( keyHolders.size() );
402  for ( const auto& holder : keyHolders ) {
403  extractKey( validOutput, ignoredOutput, holder );
404  }
405  }
406 
407  template <typename KeyHolderColl>
408  void collectDependencies( const KeyHolderColl keyHolderArray[] )
409  {
410  for ( size_t i = 0; i < NUM_ACCESS_MODES; ++i ) {
411  extractKeys( m_dataDepKeys[i], m_ignoredDataDeps[i], keyHolderArray[i] );
412  }
413  }
414 
421  {
422  DataObjIDColl& validInputs = m_dataDepKeys[accessToIndex( AccessMode::Read )];
423  DataObjIDColl& validOutputs = m_dataDepKeys[accessToIndex( AccessMode::Write )];
424  DataObjIDColl& ignoredInputs = m_ignoredDataDeps[accessToIndex( AccessMode::Read )];
425  DataObjIDColl& ignoredOutputs = m_ignoredDataDeps[accessToIndex( AccessMode::Write )];
426 
427  for ( auto handle : legacyHandles ) {
428  if ( handle->mode() & Gaudi::v1::DataHandle::Reader ) {
429  extractKey( validInputs, ignoredInputs, handle );
430  }
431  if ( handle->mode() & Gaudi::v1::DataHandle::Writer ) {
432  extractKey( validOutputs, ignoredOutputs, handle );
433  }
434  }
435  }
436 
438  static void updateKey( Gaudi::v2::DataHandle* target, DataObjID&& key ) { target->setTargetKey( std::move( key ) ); }
439 
441  static void updateKey( DataObjID& target, DataObjID&& key ) { target = std::move( key ); }
442 
444  static void updateKey( Gaudi::v1::DataHandle* target, DataObjID&& key ) { target->setKey( std::move( key ) ); }
445 
452  template <typename KeyHolderColl>
453  static void updateDependencies( KeyHolderColl& keyHolders, const DataObjIDMapping& keyMap )
454  {
455  for ( auto& holder : keyHolders ) {
456  const DataObjID& oldKey = accessKey( holder );
457  auto mappedKey = keyMap( oldKey );
458  if ( mappedKey ) {
459  updateKey( holder, *std::move( mappedKey ) );
460  }
461  }
462  }
463 
466  static void updateDependencies( DataObjIDColl& keys, const DataObjIDMapping& keyMap )
467  {
468  DataObjIDColl result;
469  result.reserve( keys.size() );
470  for ( const auto& key : keys ) {
471  result.emplace( keyMap( key ).value_or( key ) );
472  }
473  keys = std::move( result );
474  }
475 
478  {
479  updateDependencies( keys.value(), keyMap );
480  }
481 
483  template <typename KeyHolderColl>
484  static void updateDependencies( KeyHolderColl keyHoldersArray[], const DataObjIDMapping& keyMap )
485  {
486  for ( size_t i = 0; i < NUM_ACCESS_MODES; ++i ) {
487  updateDependencies( keyHoldersArray[i], keyMap );
488  }
489  }
490 };
491 
492 #endif // !GAUDIKERNEL_DATAHANDLEHOLDERBASE
static size_t accessToIndex(AccessMode access)
Convert a data dependency access mode to its index in our internal storage.
DataHandleHolderBase(Args &&...args)
NOTE: Cannot use "using Super::Super;" due to a GCC 6 bug.
static void updateKey(Gaudi::v1::DataHandle *target, DataObjID &&key)
Update the key of a legacy (non-reentrant) DataHandle.
static void updateKey(DataObjID &target, DataObjID &&key)
Update the key of a DataObjID.
bool isNewDataDependency(const Gaudi::v2::DataHandle &handle) const
Check if a data dependency has not been declared before (new DataHandle version)
constexpr static const auto FAILURE
Definition: StatusCode.h:88
Define general base for Gaudi exception.
static void updateDependencies(KeyHolderColl keyHoldersArray[], const DataObjIDMapping &keyMap)
Specialization of the above algorithm for arrays of KeyHolderColls.
void collectDependencies(const KeyHolderColl keyHolderArray[])
void initializeDataHandleHolder()
Initialize the DataHandles.
Implementation of property with value of concrete type.
Definition: Property.h:383
static void updateKey(Gaudi::v2::DataHandle *target, DataObjID &&key)
Update the key of a DataHandle.
IDataHandleMetadata::AccessMode AccessMode
Definition: DataHandle.h:115
void addDataDependency(const DataObjID &key, AccessMode access) final override
Add a data dependency, even after initialization.
const DataObjID & targetKey() const
(Configurable) ID of the data being accessed via this handle
Definition: DataHandle.h:133
virtual IDataHandleHolder * owner() const
Definition: DataHandle.h:48
STL namespace.
void renounce(Gaudi::v1::DataHandle &handle) override
Discard ownership of a legacy DataHandle.
std::unordered_set< Gaudi::v1::DataHandle * > m_legacyHandles
void updateDataDependencies(const DataObjIDMapping &keyMap)
Update the key of each registered data dependency, using a user-defined mapping from the old to the n...
const DataObjIDColl & ignoredDataDependencies(AccessMode access) const
Tell which data dependencies have been ignored due to an empty key.
static void updateDependencies(Gaudi::Property< DataObjIDColl > &keys, const DataObjIDMapping &keyMap)
Specialization for properties which accesses the inner collection.
const DataObjIDColl * allDataDependencies() const final override
Entity which holds DataHandles and can track the associated data dependencies for the Scheduler...
virtual const DataObjIDColl * allDataDependencies() const =0
Access the internal array of data dependencies.
void registerDataHandle(Gaudi::v2::DataHandle &handle) final override
Register a data handle of this algorithm/tool.
static const DataObjID & accessKey(const DataObjID &key)
Access the key of a DataObjID (identity function)
virtual void setOwner(IDataHandleHolder *o)
Definition: DataHandle.h:47
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:51
void collectImplicitDataDependencies(const IDataHandleHolder *child)
Add the dependencies of another DataHandleHolder to our dependency list.
bool isNewDataDependency(const Gaudi::v1::DataHandle &handle) const
Check if a data dependency has not been declared before (new DataHandle version)
virtual const DataObjID & fullKey() const
Definition: DataHandle.h:56
Common infrastructure for classes that manage data handles and other Scheduler-known data dependencie...
void declare(Gaudi::v1::DataHandle &handle) override
Declare ownership of a legacy DataHandle.
T erase(T...args)
virtual Mode mode() const
Definition: DataHandle.h:50
Base class to all new-style data handles.
Definition: DataHandle.h:91
static void updateDependencies(KeyHolderColl &keyHolders, const DataObjIDMapping &keyMap)
Update the DataObjID keys of a collection of key holders.
virtual void setKey(const DataObjID &key) const
Definition: DataHandle.h:52
static const DataObjID & accessKey(const Gaudi::v1::DataHandle *handlePtr)
Access the key of a legacy (non-reentrant) DataHandle.
void setTargetKey(const DataObjID &id)
Change the ID of the target data.
Definition: DataHandle.h:136
T move(T...args)
constexpr static const auto SUCCESS
Definition: StatusCode.h:87
T insert(T...args)
T find(T...args)
T size(T...args)
bool empty() const
Tell if this DataObjID is has an empty key.
Definition: DataObjID.h:53
Base class used to extend a class implementing other interfaces.
Definition: extends.h:10
static void updateDependencies(DataObjIDColl &keys, const DataObjIDMapping &keyMap)
Specialization of the above algorithm for DataObjIDColls, where updating the keys in place is not pos...
T emplace(T...args)
static void extractKey(DataObjIDColl &validOutput, DataObjIDColl &ignoredOutput, const KeyHolder &holder)
Extract the key of a key holder, putting empty keys on an ignore list.
const DataObjIDColl & dataDependencies(AccessMode access) const final override
Tell which whiteboard keys the algorithm will be reading or writing.
const ValueType & value() const
Backward compatibility (.
Definition: Property.h:587
AccessMode access() const
Definition: DataHandle.h:116
StatusCode handleCircularDataDependencies(CircularDepHandler &&circularDepHandler)
Look for circular dependencies and let a user-specified handler deal with each of them...
#define GAUDI_API
Definition: Kernel.h:71
static void extractKeys(DataObjIDColl &validOutput, DataObjIDColl &ignoredOutput, const KeyHolderColl &keyHolders)
Extract non-empty DataObjID keys from a collection of key holders.
void collectExplicitDataDependencies()
Collect all explicit data dependencies in a single place.
static const DataObjID & accessKey(const Gaudi::v2::DataHandle *handlePtr)
Access the key of a DataHandle.
void collectDependencies(const std::unordered_set< Gaudi::v1::DataHandle * > legacyHandles)
Specialization of extractDependencies for legacy DataHandles.
T reserve(T...args)