CMS 3D CMS Logo

PathsAndConsumesOfModules.cc
Go to the documentation of this file.
2 
6 
8 
9 #include <algorithm>
10 #include <limits>
11 #include <unordered_set>
12 namespace edm {
13 
16 
17  void PathsAndConsumesOfModules::initialize(Schedule const* schedule, std::shared_ptr<ProductRegistry const> preg) {
19  preg_ = preg;
20 
21  paths_.clear();
22  schedule->triggerPaths(paths_);
23 
24  endPaths_.clear();
25  schedule->endPaths(endPaths_);
26 
27  modulesOnPaths_.resize(paths_.size());
28  unsigned int i = 0;
29  unsigned int hint = 0;
30  for (auto const& path : paths_) {
31  schedule->moduleDescriptionsInPath(path, modulesOnPaths_.at(i), hint);
32  if (!modulesOnPaths_.at(i).empty())
33  ++hint;
34  ++i;
35  }
36 
37  modulesOnEndPaths_.resize(endPaths_.size());
38  i = 0;
39  hint = 0;
40  for (auto const& endpath : endPaths_) {
41  schedule->moduleDescriptionsInEndPath(endpath, modulesOnEndPaths_.at(i), hint);
42  if (!modulesOnEndPaths_.at(i).empty())
43  ++hint;
44  ++i;
45  }
46 
47  schedule->fillModuleAndConsumesInfo(allModuleDescriptions_,
51  *preg);
52  }
53 
54  void PathsAndConsumesOfModules::removeModules(std::vector<ModuleDescription const*> const& modules) {
55  // First check that no modules on Paths are removed
56  auto checkPath = [&modules](auto const& paths) {
57  for (auto const& path : paths) {
58  for (auto const& description : path) {
59  if (std::find(modules.begin(), modules.end(), description) != modules.end()) {
60  throw cms::Exception("Assert")
61  << "PathsAndConsumesOfModules::removeModules() is trying to remove a module with label "
62  << description->moduleLabel() << " id " << description->id() << " from a Path, this should not happen.";
63  }
64  }
65  }
66  };
69 
70  // Remove the modules and adjust the indices in idToIndex map
71  for (auto iModule = 0U; iModule != allModuleDescriptions_.size(); ++iModule) {
72  auto found = std::find(modules.begin(), modules.end(), allModuleDescriptions_[iModule]);
73  if (found != modules.end()) {
74  allModuleDescriptions_.erase(allModuleDescriptions_.begin() + iModule);
75  for (auto iBranchType = 0U; iBranchType != NumBranchTypes; ++iBranchType) {
76  modulesWhoseProductsAreConsumedBy_[iBranchType].erase(
77  modulesWhoseProductsAreConsumedBy_[iBranchType].begin() + iModule);
78  }
81  for (auto& idToIndex : moduleIDToIndex_) {
82  if (idToIndex.second >= iModule) {
83  idToIndex.second--;
84  }
85  }
86  --iModule;
87  }
88  }
89  }
90 
92  unsigned int moduleID) const {
94  }
95 
97  unsigned int dummy = 0;
98  auto target = std::make_pair(moduleID, dummy);
99  std::vector<std::pair<unsigned int, unsigned int>>::const_iterator iter =
101  if (iter == moduleIDToIndex_.end() || iter->first != moduleID) {
103  << "PathsAndConsumesOfModules::moduleDescription: Unknown moduleID " << moduleID << "\n";
104  }
105  return allModuleDescriptions_.at(iter->second);
106  }
107 
108  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesOnPath(unsigned int pathIndex) const {
109  return modulesOnPaths_.at(pathIndex);
110  }
111 
112  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesOnEndPath(
113  unsigned int endPathIndex) const {
114  return modulesOnEndPaths_.at(endPathIndex);
115  }
116 
117  std::vector<ModuleDescription const*> const& PathsAndConsumesOfModules::doModulesWhoseProductsAreConsumedBy(
118  unsigned int moduleID, BranchType branchType) const {
119  return modulesWhoseProductsAreConsumedBy_[branchType].at(moduleIndex(moduleID));
120  }
121 
122  std::vector<ConsumesInfo> PathsAndConsumesOfModules::doConsumesInfo(unsigned int moduleID) const {
123  Worker const* worker = schedule_->allWorkers().at(moduleIndex(moduleID));
124  return worker->consumesInfo();
125  }
126 
128  // moduleIDToIndex_ is sorted, so last element has the largest ID
129  return moduleIDToIndex_.empty() ? 0 : moduleIDToIndex_.back().first;
130  }
131 
132  unsigned int PathsAndConsumesOfModules::moduleIndex(unsigned int moduleID) const {
133  unsigned int dummy = 0;
134  auto target = std::make_pair(moduleID, dummy);
135  std::vector<std::pair<unsigned int, unsigned int>>::const_iterator iter =
137  if (iter == moduleIDToIndex_.end() || iter->first != moduleID) {
139  << "PathsAndConsumesOfModules::moduleIndex: Unknown moduleID " << moduleID << "\n";
140  }
141  return iter->second;
142  }
143 } // namespace edm
144 
145 namespace {
146  // helper function for nonConsumedUnscheduledModules,
147  void findAllConsumedModules(edm::PathsAndConsumesOfModulesBase const& iPnC,
149  std::unordered_set<unsigned int>& consumedModules) {
150  // If this node of the DAG has been processed already, no need to
151  // reprocess again
152  if (consumedModules.find(module->id()) != consumedModules.end()) {
153  return;
154  }
155  consumedModules.insert(module->id());
156  for (auto iBranchType = 0U; iBranchType != edm::NumBranchTypes; ++iBranchType) {
157  for (auto const& c :
158  iPnC.modulesWhoseProductsAreConsumedBy(module->id(), static_cast<edm::BranchType>(iBranchType))) {
159  findAllConsumedModules(iPnC, c, consumedModules);
160  }
161  }
162  }
163 } // namespace
164 
165 namespace edm {
166  std::vector<ModuleDescription const*> nonConsumedUnscheduledModules(
167  edm::PathsAndConsumesOfModulesBase const& iPnC, std::vector<ModuleProcessName>& consumedByChildren) {
168  const std::string kTriggerResults("TriggerResults");
169 
170  std::vector<std::string> pathNames = iPnC.paths();
171  const unsigned int kFirstEndPathIndex = pathNames.size();
172  pathNames.insert(pathNames.end(), iPnC.endPaths().begin(), iPnC.endPaths().end());
173 
174  // The goal is to find modules that are not depended upon by
175  // scheduled modules. To do that, we identify all modules that are
176  // depended upon by scheduled modules, and do a set subtraction.
177  //
178  // First, denote all scheduled modules (i.e. in Paths and
179  // EndPaths) as "consumers".
180  std::vector<ModuleDescription const*> consumerModules;
181  for (unsigned int pathIndex = 0; pathIndex != pathNames.size(); ++pathIndex) {
182  std::vector<ModuleDescription const*> const* moduleDescriptions;
183  if (pathIndex < kFirstEndPathIndex) {
184  moduleDescriptions = &(iPnC.modulesOnPath(pathIndex));
185  } else {
186  moduleDescriptions = &(iPnC.modulesOnEndPath(pathIndex - kFirstEndPathIndex));
187  }
188  std::copy(moduleDescriptions->begin(), moduleDescriptions->end(), std::back_inserter(consumerModules));
189  }
190 
191  // Then add TriggerResults, and all Paths and EndPaths themselves
192  // to the set of "consumers" (even if they don't depend on any
193  // data products, they must not be deleted). Also add anything
194  // consumed by child SubProcesses to the set of "consumers".
195  auto const& allModules = iPnC.allModules();
196  for (auto const& description : allModules) {
197  if (description->moduleLabel() == kTriggerResults or
198  std::find(pathNames.begin(), pathNames.end(), description->moduleLabel()) != pathNames.end()) {
199  consumerModules.push_back(description);
200  } else if (std::binary_search(consumedByChildren.begin(),
201  consumedByChildren.end(),
202  ModuleProcessName{description->moduleLabel(), description->processName()}) or
203  std::binary_search(consumedByChildren.begin(),
204  consumedByChildren.end(),
205  ModuleProcessName{description->moduleLabel(), ""})) {
206  consumerModules.push_back(description);
207  }
208  }
209 
210  // Find modules that have any data dependence path to any module
211  // in consumerModules.
212  std::unordered_set<unsigned int> consumedModules;
213  for (auto& description : consumerModules) {
214  findAllConsumedModules(iPnC, description, consumedModules);
215  }
216 
217  // All other modules will then be classified as non-consumed, even
218  // if they would have dependencies within them.
219  std::vector<ModuleDescription const*> unusedModules;
220  std::copy_if(allModules.begin(),
221  allModules.end(),
222  std::back_inserter(unusedModules),
223  [&consumedModules](ModuleDescription const* description) {
224  return consumedModules.find(description->id()) == consumedModules.end();
225  });
226  return unusedModules;
227  }
228 
229  //====================================
230  // checkForCorrectness algorithm
231  //
232  // The code creates a 'dependency' graph between all
233  // modules. A module depends on another module if
234  // 1) it 'consumes' data produced by that module
235  // 2) it appears directly after the module within a Path
236  //
237  // If there is a cycle in the 'dependency' graph then
238  // the schedule may be unrunnable. The schedule is still
239  // runnable if all cycles have at least two edges which
240  // connect modules only by Path dependencies (i.e. not
241  // linked by a data dependency).
242  //
243  // Example 1:
244  // C consumes data from B
245  // Path 1: A + B + C
246  // Path 2: B + C + A
247  //
248  // Cycle: A after C [p2], C consumes B, B after A [p1]
249  // Since this cycle has 2 path only edges it is OK since
250  // A and (B+C) are independent so their run order doesn't matter
251  //
252  // Example 2:
253  // B consumes A
254  // C consumes B
255  // Path: C + A
256  //
257  // Cycle: A after C [p], C consumes B, B consumes A
258  // Since this cycle has 1 path only edge it is unrunnable.
259  //
260  // Example 3:
261  // A consumes B
262  // B consumes C
263  // C consumes A
264  // (no Path since unscheduled execution)
265  //
266  // Cycle: A consumes B, B consumes C, C consumes A
267  // Since this cycle has 0 path only edges it is unrunnable.
268  //====================================
269 
270  namespace {
271  struct ModuleStatus {
272  std::vector<unsigned int> dependsOn_;
273  std::vector<unsigned int> pathsOn_;
274  unsigned long long lastSearch = 0;
275  bool onPath_ = false;
276  bool wasRun_ = false;
277  };
278 
279  struct PathStatus {
280  std::vector<unsigned int> modulesOnPath_;
281  unsigned long int activeModuleSlot_ = 0;
282  unsigned long int nModules_ = 0;
283  unsigned int index_ = 0;
284  bool endPath_ = false;
285  };
286 
287  class CircularDependencyException {};
288 
289  bool checkIfCanRun(unsigned long long searchIndex,
290  unsigned int iModuleToCheckID,
291  std::vector<ModuleStatus>& iModules,
292  std::vector<unsigned int>& stackTrace) {
293  auto& status = iModules[iModuleToCheckID];
294  if (status.wasRun_) {
295  return true;
296  }
297 
298  if (status.lastSearch == searchIndex) {
299  //check to see if the module is already on the stack
300  // checking searchIndex is insufficient as multiple modules
301  // in this search may be dependent upon the same module
302  auto itFound = std::find(stackTrace.begin(), stackTrace.end(), iModuleToCheckID);
303  if (itFound != stackTrace.end()) {
304  stackTrace.push_back(iModuleToCheckID);
305  throw CircularDependencyException();
306  }
307  //we have already checked this module's dependencies during this search
308  return false;
309  }
310  stackTrace.push_back(iModuleToCheckID);
311  status.lastSearch = searchIndex;
312 
313  bool allDependenciesRan = true;
314  for (auto index : status.dependsOn_) {
315  auto& dep = iModules[index];
316  if (dep.onPath_) {
317  if (not dep.wasRun_) {
318  allDependenciesRan = false;
319  }
320  } else if (not checkIfCanRun(searchIndex, index, iModules, stackTrace)) {
321  allDependenciesRan = false;
322  }
323  }
324  if (allDependenciesRan) {
325  status.wasRun_ = true;
326  }
327  stackTrace.pop_back();
328 
329  return allDependenciesRan;
330  }
331 
332  void findAllDependenciesForModule(unsigned int iModID,
333  std::vector<ModuleStatus> const& iStatus,
334  std::vector<std::unordered_set<unsigned int>>& oDependencies) {
335  auto const& dependsOn = iStatus[iModID].dependsOn_;
336  if (dependsOn.empty() or !oDependencies[iModID].empty()) {
337  return;
338  }
339  oDependencies[iModID].insert(dependsOn.begin(), dependsOn.end());
340  for (auto dep : dependsOn) {
341  findAllDependenciesForModule(dep, iStatus, oDependencies);
342  oDependencies[iModID].merge(oDependencies[dep]);
343  }
344  }
345  std::vector<std::unordered_set<unsigned int>> findAllDependenciesForModules(
346  std::vector<ModuleStatus> const& iStatus) {
347  std::vector<std::unordered_set<unsigned int>> ret(iStatus.size());
348  for (unsigned int id = 0; id < iStatus.size(); ++id) {
349  findAllDependenciesForModule(id, iStatus, ret);
350  }
351  return ret;
352  }
353  } // namespace
354  void checkForModuleDependencyCorrectness(edm::PathsAndConsumesOfModulesBase const& iPnC, bool iPrintDependencies) {
356 
357  //Need to lookup ids to names quickly
358  std::unordered_map<unsigned int, std::string> moduleIndexToNames;
359 
360  std::unordered_map<std::string, unsigned int> pathStatusInserterModuleLabelToModuleID;
361 
362  //for testing, state that TriggerResults is at the end of all paths
363  const std::string kTriggerResults("TriggerResults");
364  const std::string kPathStatusInserter("PathStatusInserter");
365  const std::string kEndPathStatusInserter("EndPathStatusInserter");
366  unsigned int kTriggerResultsIndex = kInvalidIndex;
367  ModuleStatus triggerResultsStatus;
368  unsigned int largestIndex = 0;
369  for (auto const& description : iPnC.allModules()) {
370  moduleIndexToNames.insert(std::make_pair(description->id(), description->moduleLabel()));
371  if (kTriggerResults == description->moduleLabel()) {
372  kTriggerResultsIndex = description->id();
373  }
374  if (description->id() > largestIndex) {
375  largestIndex = description->id();
376  }
377  if (description->moduleName() == kPathStatusInserter) {
378  triggerResultsStatus.dependsOn_.push_back(description->id());
379  }
380  if (description->moduleName() == kPathStatusInserter || description->moduleName() == kEndPathStatusInserter) {
381  pathStatusInserterModuleLabelToModuleID[description->moduleLabel()] = description->id();
382  }
383  }
384 
385  std::vector<ModuleStatus> statusOfModules(largestIndex + 1);
386  for (auto const& nameID : pathStatusInserterModuleLabelToModuleID) {
387  statusOfModules[nameID.second].onPath_ = true;
388  unsigned int pathIndex;
389  auto const& paths = iPnC.paths();
390  auto itFound = std::find(paths.begin(), paths.end(), nameID.first);
391  if (itFound != paths.end()) {
392  pathIndex = itFound - paths.begin();
393  } else {
394  auto const& endPaths = iPnC.endPaths();
395  itFound = std::find(endPaths.begin(), endPaths.end(), nameID.first);
396  assert(itFound != endPaths.end());
397  pathIndex = itFound - endPaths.begin() + iPnC.paths().size();
398  }
399  statusOfModules[nameID.second].pathsOn_.push_back(pathIndex);
400  }
401  if (kTriggerResultsIndex != kInvalidIndex) {
402  statusOfModules[kTriggerResultsIndex] = std::move(triggerResultsStatus);
403  }
404 
405  std::vector<PathStatus> statusOfPaths(iPnC.paths().size() + iPnC.endPaths().size());
406 
407  //If there are no paths, no modules will run so nothing to check
408  if (statusOfPaths.empty()) {
409  return;
410  }
411 
412  {
413  auto nPaths = iPnC.paths().size();
414  for (unsigned int p = 0; p < nPaths; ++p) {
415  auto& status = statusOfPaths[p];
416  status.index_ = p;
417  status.modulesOnPath_.reserve(iPnC.modulesOnPath(p).size() + 1);
418  std::unordered_set<unsigned int> uniqueModules;
419  for (auto const& mod : iPnC.modulesOnPath(p)) {
420  if (uniqueModules.insert(mod->id()).second) {
421  status.modulesOnPath_.push_back(mod->id());
422  statusOfModules[mod->id()].onPath_ = true;
423  statusOfModules[mod->id()].pathsOn_.push_back(p);
424  }
425  }
426  status.nModules_ = uniqueModules.size() + 1;
427 
428  //add the PathStatusInserter at the end
429  auto found = pathStatusInserterModuleLabelToModuleID.find(iPnC.paths()[p]);
430  assert(found != pathStatusInserterModuleLabelToModuleID.end());
431  status.modulesOnPath_.push_back(found->second);
432  }
433  }
434  {
435  auto offset = iPnC.paths().size();
436  auto nPaths = iPnC.endPaths().size();
437  for (unsigned int p = 0; p < nPaths; ++p) {
438  auto& status = statusOfPaths[p + offset];
439  status.endPath_ = true;
440  status.index_ = p;
441  status.modulesOnPath_.reserve(iPnC.modulesOnEndPath(p).size() + 1);
442  std::unordered_set<unsigned int> uniqueModules;
443  for (auto const& mod : iPnC.modulesOnEndPath(p)) {
444  if (uniqueModules.insert(mod->id()).second) {
445  status.modulesOnPath_.push_back(mod->id());
446  statusOfModules[mod->id()].onPath_ = true;
447  statusOfModules[mod->id()].pathsOn_.push_back(p + offset);
448  }
449  }
450  status.nModules_ = uniqueModules.size();
451 
452  //add the EndPathStatusInserter at the end
453  auto found = pathStatusInserterModuleLabelToModuleID.find(iPnC.endPaths()[p]);
454  if (found != pathStatusInserterModuleLabelToModuleID.end()) {
455  status.modulesOnPath_.push_back(found->second);
456  ++status.nModules_;
457  }
458  }
459  }
460 
461  for (auto const& description : iPnC.allModules()) {
462  unsigned int const moduleIndex = description->id();
463  auto const& dependentModules = iPnC.modulesWhoseProductsAreConsumedBy(moduleIndex);
464  auto& deps = statusOfModules[moduleIndex];
465  deps.dependsOn_.reserve(dependentModules.size());
466  for (auto const& depDescription : dependentModules) {
467  if (iPrintDependencies) {
468  edm::LogAbsolute("ModuleDependency")
469  << "ModuleDependency '" << description->moduleLabel() << "' depends on data products from module '"
470  << depDescription->moduleLabel() << "'";
471  }
472  deps.dependsOn_.push_back(depDescription->id());
473  }
474  }
475 
476  unsigned int nPathsFinished = 0;
477  for (auto const& status : statusOfPaths) {
478  if (status.nModules_ == 0) {
479  ++nPathsFinished;
480  }
481  }
482 
483  //if a circular dependency exception happens, stackTrace has the info
484  std::vector<unsigned int> stackTrace;
485  bool madeForwardProgress = true;
486  try {
487  //'simulate' the running of the paths. On each step mark each module as 'run'
488  // if all the module's dependencies were fulfilled in a previous step
489  unsigned long long searchIndex = 0;
490  while (madeForwardProgress and nPathsFinished != statusOfPaths.size()) {
491  madeForwardProgress = false;
492  for (auto& p : statusOfPaths) {
493  //the path has already completed in an earlier pass
494  if (p.activeModuleSlot_ == p.nModules_) {
495  continue;
496  }
497  ++searchIndex;
498  bool didRun = checkIfCanRun(searchIndex, p.modulesOnPath_[p.activeModuleSlot_], statusOfModules, stackTrace);
499  if (didRun) {
500  madeForwardProgress = true;
501  ++p.activeModuleSlot_;
502  if (p.activeModuleSlot_ == p.nModules_) {
503  ++nPathsFinished;
504  }
505  }
506  }
507  }
508  } catch (CircularDependencyException const&) {
509  //the last element in stackTrace must appear somewhere earlier in stackTrace
510  std::ostringstream oStr;
511 
512  unsigned int lastIndex = stackTrace.front();
513  bool firstSkipped = false;
514  for (auto id : stackTrace) {
515  if (firstSkipped) {
516  oStr << " module '" << moduleIndexToNames[lastIndex] << "' depends on " << moduleIndexToNames[id] << "\n";
517  } else {
518  firstSkipped = true;
519  }
520  lastIndex = id;
521  }
522  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
523  << "Circular module dependency found in configuration\n"
524  << oStr.str();
525  }
526 
527  auto pathName = [&](PathStatus const& iP) {
528  if (iP.endPath_) {
529  return iPnC.endPaths()[iP.index_];
530  }
531  return iPnC.paths()[iP.index_];
532  };
533 
534  //The program would deadlock
535  if (not madeForwardProgress) {
536  std::ostringstream oStr;
537  auto modIndex = std::numeric_limits<unsigned int>::max();
538  unsigned int presentPath;
539  for (auto itP = statusOfPaths.begin(); itP != statusOfPaths.end(); ++itP) {
540  auto const& p = *itP;
541  if (p.activeModuleSlot_ == p.nModules_) {
542  continue;
543  }
544  //this path is stuck
545  modIndex = p.modulesOnPath_[p.activeModuleSlot_];
546  presentPath = itP - statusOfPaths.begin();
547  break;
548  }
549  //NOTE the following should always be true as at least 1 path should be stuc.
550  // I've added the condition just to be paranoid.
551  if (modIndex != std::numeric_limits<unsigned int>::max()) {
552  struct ProgressInfo {
553  ProgressInfo(unsigned int iMod, unsigned int iPath, bool iPreceeds = false)
554  : moduleIndex_(iMod), pathIndex_(iPath), preceeds_(iPreceeds) {}
555 
556  ProgressInfo(unsigned int iMod) : moduleIndex_(iMod), pathIndex_{}, preceeds_(false) {}
557 
558  unsigned int moduleIndex_ = std::numeric_limits<unsigned int>::max();
559  std::optional<unsigned int> pathIndex_;
560  bool preceeds_;
561 
562  bool operator==(ProgressInfo const& iOther) const {
563  return moduleIndex_ == iOther.moduleIndex_ and pathIndex_ == iOther.pathIndex_;
564  }
565  };
566 
567  std::vector<ProgressInfo> progressTrace;
568  progressTrace.emplace_back(modIndex, presentPath);
569 
570  //The following starts from the first found unrun module on a path. It then finds
571  // the first modules it depends on that was not run. If that module is on a Task
572  // it then repeats the check for that module's dependencies. If that module is on
573  // a path, it checks to see if that module is the first unrun module of a path
574  // and if so it repeats the check for that module's dependencies, if not it
575  // checks the dependencies of the stuck module on that path.
576  // Eventually, all these checks should allow us to find a cycle of modules.
577 
578  //NOTE: the only way foundUnrunModule should ever by false by the end of the
579  // do{}while loop is if there is a bug in the algorithm. I've included it to
580  // try to avoid that case causing an infinite loop in the program.
581  bool foundUnrunModule;
582  do {
583  //check dependencies looking for stuff not run and on a path
584  foundUnrunModule = false;
585  for (auto depMod : statusOfModules[modIndex].dependsOn_) {
586  auto const& depStatus = statusOfModules[depMod];
587  if (not depStatus.wasRun_ and depStatus.onPath_) {
588  foundUnrunModule = true;
589  //last run on a path?
590  bool lastOnPath = false;
591  unsigned int foundPath;
592  for (auto pathOn : depStatus.pathsOn_) {
593  auto const& depPaths = statusOfPaths[pathOn];
594  if (depPaths.modulesOnPath_[depPaths.activeModuleSlot_] == depMod) {
595  lastOnPath = true;
596  foundPath = pathOn;
597  break;
598  }
599  }
600  if (lastOnPath) {
601  modIndex = depMod;
602  progressTrace.emplace_back(modIndex, foundPath);
603  } else {
604  //some earlier module on the same path is stuck
605  progressTrace.emplace_back(depMod, depStatus.pathsOn_[0]);
606  auto const& depPath = statusOfPaths[depStatus.pathsOn_[0]];
607  modIndex = depPath.modulesOnPath_[depPath.activeModuleSlot_];
608  progressTrace.emplace_back(modIndex, depStatus.pathsOn_[0], true);
609  }
610  break;
611  }
612  }
613  if (not foundUnrunModule) {
614  //check unscheduled modules
615  for (auto depMod : statusOfModules[modIndex].dependsOn_) {
616  auto const& depStatus = statusOfModules[depMod];
617  if (not depStatus.wasRun_ and not depStatus.onPath_) {
618  foundUnrunModule = true;
619  progressTrace.emplace_back(depMod);
620  modIndex = depMod;
621  break;
622  }
623  }
624  }
625  } while (foundUnrunModule and (0 == std::count(progressTrace.begin(),
626  progressTrace.begin() + progressTrace.size() - 1,
627  progressTrace.back())));
628 
629  auto printTrace = [&](auto& oStr, auto itBegin, auto itEnd) {
630  for (auto itTrace = itBegin; itTrace != itEnd; ++itTrace) {
631  if (itTrace != itBegin) {
632  if (itTrace->preceeds_) {
633  oStr << " and follows module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' on the path\n";
634  } else {
635  oStr << " and depends on module '" << moduleIndexToNames[itTrace->moduleIndex_] << "'\n";
636  }
637  }
638  if (itTrace + 1 != itEnd) {
639  if (itTrace->pathIndex_) {
640  oStr << " module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' is on path '"
641  << pathName(statusOfPaths[*itTrace->pathIndex_]) << "'";
642  } else {
643  oStr << " module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' is in a task";
644  }
645  }
646  }
647  };
648 
649  if (not foundUnrunModule) {
650  //If we get here, this suggests a problem with either the algorithm that finds problems or the algorithm
651  // that attempts to report the problem
652  oStr << "Algorithm Error, unable to find problem. Contact framework group.\n Traced problem this far\n";
653  printTrace(oStr, progressTrace.begin(), progressTrace.end());
654  } else {
655  printTrace(
656  oStr, std::find(progressTrace.begin(), progressTrace.end(), progressTrace.back()), progressTrace.end());
657  }
658  }
659  //the schedule deadlocked
660  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
661  << "The Path/EndPath configuration could cause the job to deadlock\n"
662  << oStr.str();
663  }
664 
665  //NOTE: although the following conditions are not needed for safe running, they are
666  // policy choices the collaboration has made.
667 
668  //HLT wants all paths to be equivalent. If a path has a module A that needs data from module B and module B appears on one path
669  // as module A then B must appear on ALL paths that have A.
670  unsigned int modIndex = 0;
671  for (auto& mod : statusOfModules) {
672  for (auto& depIndex : mod.dependsOn_) {
673  std::size_t count = 0;
674  std::size_t nonEndPaths = 0;
675  for (auto modPathID : mod.pathsOn_) {
676  if (statusOfPaths[modPathID].endPath_) {
677  continue;
678  }
679  ++nonEndPaths;
680  for (auto depPathID : statusOfModules[depIndex].pathsOn_) {
681  if (depPathID == modPathID) {
682  ++count;
683  break;
684  }
685  }
686  }
687  if (count != 0 and count != nonEndPaths) {
688  std::ostringstream onStr;
689  std::ostringstream missingStr;
690 
691  for (auto modPathID : mod.pathsOn_) {
692  if (statusOfPaths[modPathID].endPath_) {
693  continue;
694  }
695  bool found = false;
696  for (auto depPathID : statusOfModules[depIndex].pathsOn_) {
697  if (depPathID == modPathID) {
698  found = true;
699  }
700  }
701  auto& s = statusOfPaths[modPathID];
702  if (found) {
703  onStr << pathName(s) << " ";
704  } else {
705  missingStr << pathName(s) << " ";
706  }
707  }
708  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
709  << "Paths are non consistent\n"
710  << " module '" << moduleIndexToNames[modIndex] << "' depends on '" << moduleIndexToNames[depIndex]
711  << "' which appears on paths\n " << onStr.str() << "\nbut is missing from\n " << missingStr.str();
712  }
713  }
714  ++modIndex;
715  }
716 
717  //Check to see if for each path if the order of the modules is correct based on dependencies
718  auto allDependencies = findAllDependenciesForModules(statusOfModules);
719  for (auto& p : statusOfPaths) {
720  for (unsigned long int i = 0; p.nModules_ > 0 and i < p.nModules_ - 1; ++i) {
721  auto moduleID = p.modulesOnPath_[i];
722  if (not allDependencies[moduleID].empty()) {
723  for (unsigned long int j = i + 1; j < p.nModules_; ++j) {
724  auto testModuleID = p.modulesOnPath_[j];
725  if (allDependencies[moduleID].find(testModuleID) != allDependencies[moduleID].end()) {
726  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
727  << "Dependent module later on Path\n"
728  << " module '" << moduleIndexToNames[moduleID] << "' depends on '"
729  << moduleIndexToNames[testModuleID] << "' which is later on path " << pathName(p);
730  }
731  }
732  }
733  }
734  }
735  }
736 } // namespace edm
constexpr bool operator==(ELseverityLevel const &e1, ELseverityLevel const &e2) noexcept
depends_on::OneHolder< T, TDependsOnRecord > dependsOn(void(T::*iT)(const TDependsOnRecord &))
std::vector< ModuleDescription const * > const & allModules() const
ret
prodAgent to be discontinued
unsigned int doLargestModuleID() const override
std::vector< ModuleDescription const * > const & modulesOnPath(unsigned int pathIndex) const
std::array< std::vector< std::vector< ModuleDescription const * > >, NumBranchTypes > modulesWhoseProductsAreConsumedBy_
std::vector< ModuleProcessName > const & modulesInPreviousProcessesWhoseProductsAreConsumedBy(unsigned int moduleID) const
constexpr auto kInvalidIndex
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
std::vector< std::pair< unsigned int, unsigned int > > moduleIDToIndex_
assert(be >=bs)
std::vector< ModuleDescription const * > const & doModulesWhoseProductsAreConsumedBy(unsigned int moduleID, BranchType branchType) const override
std::vector< ModuleDescription const * > allModuleDescriptions_
ModuleDescription const * doModuleDescription(unsigned int moduleID) const override
BranchType
Definition: BranchType.h:11
std::vector< std::string > const & endPaths() const
std::vector< ModuleDescription const * > const & modulesOnEndPath(unsigned int endPathIndex) const
U second(std::pair< T, U > const &p)
std::vector< std::vector< ModuleDescription const * > > modulesOnPaths_
std::vector< std::vector< ModuleProcessName > > modulesInPreviousProcessesWhoseProductsAreConsumedBy_
checkPath
Definition: config.py:11
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
std::vector< ModuleDescription const * > const & doModulesOnPath(unsigned int pathIndex) const override
virtual std::vector< ConsumesInfo > consumesInfo() const =0
void initialize(Schedule const *, std::shared_ptr< ProductRegistry const >)
std::vector< ModuleDescription const * > nonConsumedUnscheduledModules(edm::PathsAndConsumesOfModulesBase const &iPnC, std::vector< ModuleProcessName > &consumedByChildren)
static std::atomic< unsigned int > lastIndex
Definition: DDValue.cc:12
std::vector< std::string > endPaths_
void checkForModuleDependencyCorrectness(edm::PathsAndConsumesOfModulesBase const &iPnC, bool iPrintDependencies)
AllWorkers const & allWorkers() const
returns the collection of pointers to workers
Definition: Schedule.cc:1292
std::shared_ptr< ProductRegistry const > preg_
HLT enums.
unsigned int moduleIndex(unsigned int moduleID) const
void removeModules(std::vector< ModuleDescription const *> const &modules)
HLTPathStatus PathStatus
Definition: PathStatus.h:7
std::vector< ConsumesInfo > doConsumesInfo(unsigned int moduleID) const override
std::vector< ModuleDescription const * > const & modulesWhoseProductsAreConsumedBy(unsigned int moduleID, BranchType branchType=InEvent) const
Log< level::System, true > LogAbsolute
std::vector< ModuleDescription const * > const & doModulesOnEndPath(unsigned int endPathIndex) const override
std::vector< std::vector< ModuleDescription const * > > modulesOnEndPaths_
T mod(const T &a, const T &b)
Definition: ecalDccMap.h:4
def move(src, dest)
Definition: eostools.py:511
std::vector< std::string > const & paths() const