Belle II Software development
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
20using 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
24REG_MODULE(SubEvent);
25
26
27SubEventModule::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
48SubEventModule::~SubEventModule() = default;
49
50void 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 + ")");
57}
58
59void 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;
68}
69
71{
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()) {
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?
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
133void 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();
217
218 //don't screw up statistics for this module
219 processStatistics->startModule();
220 processStatistics->resumeGlobal();
221}
223{
225 processStatistics->suspendGlobal();
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
const std::string & getName() const
Returns the name of the module.
Definition: Module.h:187
@ 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
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
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
const std::vector< ModuleCondition > & getAllConditions() const
Return all set conditions for this module.
Definition: Module.h:324
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
const std::string & getName() const
Return name under which the object is saved in the DataStore.
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
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.
void initSubEvent(const std::string &objectName, const std::string &loopOver, std::shared_ptr< Path > path)
used by Path::forEach() to actually set parameters.
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()
std::shared_ptr< Path > PathPtr
Defines a pointer to a path object as a boost shared pointer.
Definition: Path.h:35
std::shared_ptr< Module > ModulePtr
Defines a pointer to a module object as a boost shared pointer.
Definition: Module.h:43
std::list< ModulePtr > ModulePtrList
Defines a std::list of shared module pointers.
Definition: Module.h:584
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
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