Belle II Software  release-05-02-19
SubEventModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2014 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Christian Pulvermacher *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <framework/core/SubEventModule.h>
12 
13 #include <framework/core/ModuleManager.h>
14 #include <framework/core/ProcessStatistics.h>
15 #include <framework/pcore/ProcHandler.h>
16 #include <framework/datastore/DataStore.h>
17 #include <framework/datastore/StoreObjPtr.h>
18 
19 #include <utility>
20 #include <memory>
21 
22 using namespace Belle2;
23 
24 //REG_MODLUE needed for --execute-path functionality
25 //Note: should not appear in module list since we're not in the right directory
26 REG_MODULE(SubEvent)
27 
28 
29 SubEventModule::SubEventModule(): Module(), EventProcessor()
30 {
31  // we need to deserialize a pickled execution path so this module needs to work
32  // when using Path.add_module() directly ...
33  setDescription(R"DOC(Internal module to handle Path.for_each() and Path.do_while().
34 
35  Warning:
36  Don't add this module directly with `Path.add_module` or
37  `basf2.register_module` but use `Path.for_each()` and `Path.do_while()`
38 
39  This module shouldn't appear in ``basf2 -m`` output.
40  If it does, check REG_MODULE() handling.)DOC");
41 
42  addParam("mode", m_mode, "SubEvent mode: 0 = for_each, 1 = do_while", m_mode);
43  addParam("loopOver", m_loopOverName, "Name of array to iterate over.", m_loopOverName);
44  addParam("objectName", m_objectName, "Name of the object holding the current iteration's item.", m_objectName);
45  addParam("path", m_path, "Path to execute for each iteration.", PathPtr(nullptr));
46  addParam("maxIterations", m_maxIterations, "Maximum iterations in case of do_while", m_maxIterations);
47  addParam("loopCondition", m_loopConditionString, "Loop condition in case of do_while", m_loopConditionString);
48 }
49 
50 SubEventModule::~SubEventModule() = default;
51 
52 void SubEventModule::initSubEvent(const std::string& objectName, const std::string& loopOver, std::shared_ptr<Path> path)
53 {
54  m_objectName = objectName;
55  m_loopOverName = loopOver;
56  m_path = std::move(path);
57  setName("for_each(" + objectName + " : " + loopOver + ")");
58  setProperties();
59 }
60 
61 void SubEventModule::initSubLoop(std::shared_ptr<Path> path, const std::string& condition, unsigned int maxIterations)
62 {
63  m_mode = c_DoWhile;
64  m_path = std::move(path);
65  m_maxIterations = maxIterations;
66  m_loopConditionString = condition.empty() ? "<1" : condition;
69  setProperties();
70 }
71 
73 {
74  if (!m_loopConditionString) {
75  B2FATAL("No Loop conditions specified");
76  }
77  if (!m_loopCondition) {
78  m_loopCondition = std::make_unique<ModuleCondition>(*m_loopConditionString, m_path, EAfterConditionPath::c_End);
79  }
80  // We also need the last module to get its return value. If it's already set then fine.
81  if (m_loopConditionModule) return;
82 
83  // Otherwise loop over everything
84  PathIterator iter(m_path);
85  while (!iter.isDone()) {
86  m_loopConditionModule = iter.get();
87  // Sooo, we have one problem: If the module has a condition with c_End as
88  // after condition path the execution does not resume on our loop path and
89  // the final module would not be executed and thus the return value is not
90  // set correctly. Now we could check for exactly this case and just refuse
91  // to have c_End conditions in our loop path but even if the condition is
92  // c_Continue, as soon as the conditional path contains another c_End
93  // condition processing will stop there. Which is debatable but currently
94  // the implementation. This means the only safe thing to do is to not allow any
95  // conditions in our loop for now
97  B2FATAL("Modules in a Path.do_while() cannot have any conditions");
98  //If we fix the path behavior that c_Continue will always come back
99  //correctly we could be less restrictive here and only disallow c_End
100  //conditions as those would really lead to undefined loop conditions
101  for (const auto& condition : m_loopConditionModule->getAllConditions()) {
102  if (condition.getAfterConditionPath() == ModuleCondition::EAfterConditionPath::c_End) {
103  B2FATAL("do_while(): Modules in a loop cannot have conditions which end processing");
104  }
105  }
106  }
107  iter.next();
108  }
109  //apparently no modules at all?
110  if (!m_loopConditionModule) {
111  B2FATAL("do_while(): Cannot loop over empty path");
112  }
113 
114  // If the last module has a condition attached the looping cannot be done
115  // reliably as event processing might take that conditional path.
116  // Obviously this does nothing right now until we fix the nested
117  // c_Continue->c_End inconsitency and allow some conditions
119  B2FATAL("do_while(): The last module in the loop path (" <<
120  m_loopConditionModule->getName() << ") cannot have a condition");
121  }
122 }
123 
125 {
126  m_moduleList = m_path->buildModulePathList();
127  //set c_ParallelProcessingCertified flag if _all_ modules have it set
131  else
133 }
134 
135 void restoreContents(const DataStore::StoreEntryMap& orig, DataStore::StoreEntryMap& dest)
136 {
137  for (const auto& entry : orig) {
138  // cppcheck-suppress variableScope ; accessing a map entry creates it so this is effect free
139  auto& destEntry = dest[entry.first];
140  auto& srcEntry = entry.second;
141  if (srcEntry.ptr == nullptr)
142  destEntry.ptr = nullptr;
143  }
144 }
145 
146 
148 {
149  if (!m_path) {
150  B2FATAL("SubEvent module not initialised properly.");
151  }
152 
153  if (m_mode == c_DoWhile) {
155  } else if (m_mode == c_ForEach) {
156  if (!(m_objectName && m_loopOverName)) {
157  B2FATAL("loopOver and objectName parameters not setup");
158  }
159  } else {
160  B2FATAL("Invalid SubEvent mode: " << m_mode);
161  }
162 
164  processStatistics->suspendGlobal();
165 
166  if (m_mode == c_ForEach) {
167  m_loopOver.isRequired(*m_loopOverName);
168  //register loop object (c_DontWriteOut to disable writing the object)
171  }
172 
173  m_moduleList = m_path->buildModulePathList();
175 
176  //don't screw up statistics for this module
177  processStatistics->startModule();
178  processStatistics->resumeGlobal();
179 }
180 
182 {
184  processStatistics->suspendGlobal();
185 
186  //get event map and make a deep copy of the StoreEntry objects
187  //(we want to revert changes to the StoreEntry objects, but not to the arrays/objects)
189  DataStore::StoreEntryMap persistentMapCopy = persistentMap;
190 
193  } else {
194  //we're in another process than we actually belong to, only call terminate where approriate
195  ModulePtrList tmpModuleList;
196  for (const ModulePtr& m : m_moduleList) {
197  if (m->hasProperties(c_TerminateInAllProcesses))
198  tmpModuleList.push_back(m);
199  }
200  processTerminate(tmpModuleList);
201  }
202 
203  if (m_mode == c_ForEach) {
204  restoreContents(persistentMapCopy, persistentMap);
205  }
206 
207  //don't screw up statistics for this module
208  processStatistics->startModule();
209  processStatistics->resumeGlobal();
210 }
211 
213 {
215 
217  processStatistics->suspendGlobal();
218  processBeginRun();
219 
220  //don't screw up statistics for this module
221  processStatistics->startModule();
222  processStatistics->resumeGlobal();
223 }
225 {
227  processStatistics->suspendGlobal();
228  processEndRun();
229 
230  //don't screw up statistics for this module
231  processStatistics->startModule();
232  processStatistics->resumeGlobal();
233 }
234 
236 {
237  // Nothing to do? fine, don't do anything;
238  if (m_mode == c_ForEach && m_loopOver.getEntries() == 0) return;
239 
241  // We don't want call processBeginRun/EndRun() again (we do that in beginRun()/endRun() anyways) so
242  // set the previous event meta to the current one to make sure this isn't triggered
244  //and don't reinitialize the random numbers in this path which is done after
245  //the master module ... so make sure there's none
246  m_master = nullptr;
247 
248  if (m_mode == c_ForEach) {
249  //get event map and make a deep copy of the StoreEntry objects
250  //(we want to revert changes to the StoreEntry objects, but not to the arrays/objects)
252  DataStore::StoreEntryMap eventMapCopy = eventMap;
254 
255 
256  //remember the state of the object before we loop over it
257  TObject* prevObject = objectEntry.object;
258  TObject* prevPtr = objectEntry.ptr;
259 
260  const int numEntries = m_loopOver.getEntries();
261  for (int i = 0; i < numEntries; i++) {
262  //set loopObject
263  objectEntry.object = m_loopOver[i];
264  objectEntry.ptr = m_loopOver[i];
265 
266  //stuff usually done in processCore()
267  PathIterator moduleIter(m_path);
268  processEvent(moduleIter, false);
269 
270  //restore datastore
271  restoreContents(eventMapCopy, eventMap);
272  }
273 
274  // Let's reset the object to the way it was
275  objectEntry.object = prevObject;
276  objectEntry.ptr = prevPtr;
277  } else {
278  int returnValue{false};
279  unsigned int curIteration{0};
280  do {
281  if (++curIteration > m_maxIterations) {
282  B2FATAL(getName() << ": Maximum number of " << m_maxIterations << " iterations reached");
283  }
284  //stuff usually done in processCore()
285  PathIterator moduleIter(m_path);
286  processEvent(moduleIter, false);
287  B2ASSERT("Module " << m_loopConditionModule->getName() << " did not set a return value, cannot loop",
289  returnValue = m_loopConditionModule->getReturnValue();
290  } while (m_loopCondition->evaluate(returnValue));
291  }
292 
293  //don't screw up statistics for this module
294  processStatistics->startModule();
295 }
Belle2::StoreEntry::object
TObject * object
The pointer to the actual object.
Definition: StoreEntry.h:41
Belle2::SubEventModule::m_loopCondition
std::unique_ptr< ModuleCondition > m_loopCondition
Condition object to evaluate if the loop is finished in case of doWhile()
Definition: SubEventModule.h:80
Belle2::PathIterator::next
void next()
increment.
Definition: PathIterator.h:59
Belle2::EventProcessor::processTerminate
void processTerminate(const ModulePtrList &modulePathList)
Terminates the modules.
Definition: EventProcessor.cc:430
Belle2::SubEventModule::m_maxIterations
unsigned int m_maxIterations
maximum number of iterations before giving up in case of doWhile()
Definition: SubEventModule.h:76
Belle2::SubEventModule::m_mode
int m_mode
Mode for this module.
Definition: SubEventModule.h:84
Belle2::DataStore::Instance
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
Belle2::SubEventModule::m_objectName
boost::optional< std::string > m_objectName
name of our loop variable in case of forEach.
Definition: SubEventModule.h:65
Belle2::DataStore::StoreEntryMap
std::map< std::string, StoreEntry > StoreEntryMap
Map for StoreEntries.
Definition: DataStore.h:89
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::Module::c_ParallelProcessingCertified
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:82
Belle2::SubEventModule::m_loopConditionModule
Module * m_loopConditionModule
pointer to the module to provide the returnValue in case of doWhile()
Definition: SubEventModule.h:82
Belle2::Module::hasCondition
bool hasCondition() const
Returns true if at least one condition was set for the module.
Definition: Module.h:313
Belle2::Module::c_TerminateInAllProcesses
@ c_TerminateInAllProcesses
When using parallel processing, call this module's terminate() function in all processes().
Definition: Module.h:85
Belle2::SubEventModule::m_loopOverName
boost::optional< std::string > m_loopOverName
name for m_loopOver in case of forEach.
Definition: SubEventModule.h:67
Belle2::ModuleCondition::EAfterConditionPath::c_End
@ c_End
End current event after the conditional path.
Belle2::StoreEntry
Wraps a stored array/object, stored under unique (name, durability) key.
Definition: StoreEntry.h:15
Belle2::DataStore::c_DontWriteOut
@ c_DontWriteOut
Object/array should be NOT saved by output modules.
Definition: DataStore.h:73
Belle2::SubEventModule::endRun
virtual void endRun() override
This method is called if the current run ends.
Definition: SubEventModule.cc:224
Belle2::SubEventModule::m_loopConditionString
boost::optional< std::string > m_loopConditionString
String for the condition when looping.
Definition: SubEventModule.h:78
Belle2::EventProcessor::processEvent
bool processEvent(PathIterator moduleIter, bool skipMasterModule)
Calls event() functions on all modules for the current event.
Definition: EventProcessor.cc:311
Belle2::DataStore::getStoreEntryMap
StoreEntryMap & getStoreEntryMap(EDurability durability)
Get a reference to the object/array map.
Definition: DataStore.h:321
Belle2::ModuleManager::allModulesHaveFlag
static bool allModulesHaveFlag(const std::list< std::shared_ptr< Module >> &list, unsigned int flag)
Returns true if and only if all modules in list have the given flag (or list is empty).
Definition: ModuleManager.cc:144
Belle2::Module::hasReturnValue
bool hasReturnValue() const
Return true if this module has a valid return value set.
Definition: Module.h:380
Belle2::Module::getReturnValue
int getReturnValue() const
Return the return value set by this module.
Definition: Module.h:383
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::SubEventModule::terminate
virtual void terminate() override
This method is called at the end of the event processing.
Definition: SubEventModule.cc:181
Belle2::SubEventModule::initialize
virtual void initialize() override
Initialize the Module.
Definition: SubEventModule.cc:147
Belle2::Module::setPropertyFlags
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:210
Belle2::SubEventModule::initSubLoop
void initSubLoop(std::shared_ptr< Path > path, const std::string &condition, unsigned int maxIterations)
ised by Path::doWhile() to actually set parameters
Definition: SubEventModule.cc:61
Belle2::SubEventModule::event
virtual void event() override
This method is the core of the module.
Definition: SubEventModule.cc:235
Belle2::EventProcessor::processBeginRun
void processBeginRun(bool skipDB=false)
Calls the begin run methods of all modules.
Definition: EventProcessor.cc:457
Belle2::SubEventModule::initSubEvent
void initSubEvent(const std::string &objectName, const std::string &loopOver, std::shared_ptr< Path > path)
used by Path::forEach() to actually set parameters.
Definition: SubEventModule.cc:52
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::StoreObjPtr
Type-safe access to single objects in the data store.
Definition: ParticleList.h:33
Belle2::ModulePtrList
std::list< ModulePtr > ModulePtrList
Defines a std::list of shared module pointers.
Definition: Module.h:586
Belle2::PathPtr
std::shared_ptr< Path > PathPtr
Defines a pointer to a path object as a boost shared pointer.
Definition: Path.h:30
Belle2::ModulePtr
std::shared_ptr< Module > ModulePtr
Defines a pointer to a module object as a boost shared pointer.
Definition: Module.h:42
Belle2::EventProcessor::m_moduleList
ModulePtrList m_moduleList
List of all modules in order initialized.
Definition: EventProcessor.h:164
Belle2::Module::getAllConditions
const std::vector< ModuleCondition > & getAllConditions() const
Return all set conditions for this module.
Definition: Module.h:326
Belle2::PathIterator::get
Module * get() const
dereference.
Definition: PathIterator.h:85
Belle2::SubEventModule::setDoWhileConditions
void setDoWhileConditions()
Set the necessary pointers for do_while(): the pointer to the module whose return value we'll use as ...
Definition: SubEventModule.cc:72
Belle2::ProcHandler::EvtProcID
static int EvtProcID()
Return ID of the current process.
Definition: ProcHandler.cc:243
Belle2::SubEventModule::beginRun
virtual void beginRun() override
Called when entering a new run.
Definition: SubEventModule.cc:212
Belle2::DataStore::registerEntry
bool registerEntry(const std::string &name, EDurability durability, TClass *objClass, bool array, EStoreFlags storeFlags)
Register an entry in the DataStore map.
Definition: DataStore.cc:190
Belle2::EventProcessor::processEndRun
void processEndRun()
Calls the end run methods of all modules.
Definition: EventProcessor.cc:491
Belle2::SubEventModule::m_processID
int m_processID
when using multi-processing contains the ID of the process where event() is called (in that process o...
Definition: SubEventModule.h:74
Belle2::EventProcessor::processInitialize
void processInitialize(const ModulePtrList &modulePathList, bool setEventInfo=true)
Initializes the modules.
Definition: EventProcessor.cc:232
Belle2::SubEventModule::m_loopOver
StoreArray< TObject > m_loopOver
array looped over in case of forEach
Definition: SubEventModule.h:69
Belle2::DataStore::c_Persistent
@ c_Persistent
Object is available during entire execution time.
Definition: DataStore.h:62
Belle2::StoreEntry::ptr
TObject * ptr
The pointer to the returned object, either equal to 'object' or null, depending on wether the object ...
Definition: StoreEntry.h:44
Belle2::SubEventModule::setProperties
void setProperties()
Set properties for this module based on the modules found in m_path.
Definition: SubEventModule.cc:124
Belle2::SubEventModule::m_path
std::shared_ptr< Path > m_path
Path to execute.
Definition: SubEventModule.h:71
Belle2::ProcHandler::parallelProcessingUsed
static bool parallelProcessingUsed()
Returns true if multiple processes have been spawned, false in single-core mode.
Definition: ProcHandler.cc:221
Belle2::StoreEntry::objClass
TClass * objClass
class of object.
Definition: StoreEntry.h:34
Belle2::PathIterator
Iterator over a Path (returning Module pointers).
Definition: PathIterator.h:36
Belle2::EventProcessor::m_master
const Module * m_master
The master module that determines the experiment/run/event number.
Definition: EventProcessor.h:163
Belle2::DataStore::c_Event
@ c_Event
Different object in each event, all objects/arrays are invalidated after event() function has been ca...
Definition: DataStore.h:61
Belle2::EventProcessor::m_previousEventMetaData
EventMetaData m_previousEventMetaData
Stores state of EventMetaData before it was last changed.
Definition: EventProcessor.h:176
Belle2::Module::getName
const std::string & getName() const
Returns the name of the module.
Definition: Module.h:189
Belle2::PathIterator::isDone
bool isDone() const
Are we finished iterating?
Definition: PathIterator.h:82
Belle2::Module::setName
void setName(const std::string &name)
Set the name of the module.
Definition: Module.h:216
Belle2::StoreArray::getEntries
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:226
Belle2::EventProcessor
provides the core event processing loop.
Definition: EventProcessor.h:39