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