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  } // namespace
332  void checkForModuleDependencyCorrectness(edm::PathsAndConsumesOfModulesBase const& iPnC, bool iPrintDependencies) {
334 
335  //Need to lookup ids to names quickly
336  std::unordered_map<unsigned int, std::string> moduleIndexToNames;
337 
338  std::unordered_map<std::string, unsigned int> pathStatusInserterModuleLabelToModuleID;
339 
340  //for testing, state that TriggerResults is at the end of all paths
341  const std::string kTriggerResults("TriggerResults");
342  const std::string kPathStatusInserter("PathStatusInserter");
343  const std::string kEndPathStatusInserter("EndPathStatusInserter");
344  unsigned int kTriggerResultsIndex = kInvalidIndex;
345  ModuleStatus triggerResultsStatus;
346  unsigned int largestIndex = 0;
347  for (auto const& description : iPnC.allModules()) {
348  moduleIndexToNames.insert(std::make_pair(description->id(), description->moduleLabel()));
349  if (kTriggerResults == description->moduleLabel()) {
350  kTriggerResultsIndex = description->id();
351  }
352  if (description->id() > largestIndex) {
353  largestIndex = description->id();
354  }
355  if (description->moduleName() == kPathStatusInserter) {
356  triggerResultsStatus.dependsOn_.push_back(description->id());
357  }
358  if (description->moduleName() == kPathStatusInserter || description->moduleName() == kEndPathStatusInserter) {
359  pathStatusInserterModuleLabelToModuleID[description->moduleLabel()] = description->id();
360  }
361  }
362 
363  std::vector<ModuleStatus> statusOfModules(largestIndex + 1);
364  for (auto const& nameID : pathStatusInserterModuleLabelToModuleID) {
365  statusOfModules[nameID.second].onPath_ = true;
366  unsigned int pathIndex;
367  auto const& paths = iPnC.paths();
368  auto itFound = std::find(paths.begin(), paths.end(), nameID.first);
369  if (itFound != paths.end()) {
370  pathIndex = itFound - paths.begin();
371  } else {
372  auto const& endPaths = iPnC.endPaths();
373  itFound = std::find(endPaths.begin(), endPaths.end(), nameID.first);
374  assert(itFound != endPaths.end());
375  pathIndex = itFound - endPaths.begin() + iPnC.paths().size();
376  }
377  statusOfModules[nameID.second].pathsOn_.push_back(pathIndex);
378  }
379  if (kTriggerResultsIndex != kInvalidIndex) {
380  statusOfModules[kTriggerResultsIndex] = std::move(triggerResultsStatus);
381  }
382 
383  std::vector<PathStatus> statusOfPaths(iPnC.paths().size() + iPnC.endPaths().size());
384 
385  //If there are no paths, no modules will run so nothing to check
386  if (statusOfPaths.empty()) {
387  return;
388  }
389 
390  {
391  auto nPaths = iPnC.paths().size();
392  for (unsigned int p = 0; p < nPaths; ++p) {
393  auto& status = statusOfPaths[p];
394  status.index_ = p;
395  status.modulesOnPath_.reserve(iPnC.modulesOnPath(p).size() + 1);
396  std::unordered_set<unsigned int> uniqueModules;
397  for (auto const& mod : iPnC.modulesOnPath(p)) {
398  if (uniqueModules.insert(mod->id()).second) {
399  status.modulesOnPath_.push_back(mod->id());
400  statusOfModules[mod->id()].onPath_ = true;
401  statusOfModules[mod->id()].pathsOn_.push_back(p);
402  }
403  }
404  status.nModules_ = uniqueModules.size() + 1;
405 
406  //add the PathStatusInserter at the end
407  auto found = pathStatusInserterModuleLabelToModuleID.find(iPnC.paths()[p]);
408  assert(found != pathStatusInserterModuleLabelToModuleID.end());
409  status.modulesOnPath_.push_back(found->second);
410  }
411  }
412  {
413  auto offset = iPnC.paths().size();
414  auto nPaths = iPnC.endPaths().size();
415  for (unsigned int p = 0; p < nPaths; ++p) {
416  auto& status = statusOfPaths[p + offset];
417  status.endPath_ = true;
418  status.index_ = p;
419  status.modulesOnPath_.reserve(iPnC.modulesOnEndPath(p).size() + 1);
420  std::unordered_set<unsigned int> uniqueModules;
421  for (auto const& mod : iPnC.modulesOnEndPath(p)) {
422  if (uniqueModules.insert(mod->id()).second) {
423  status.modulesOnPath_.push_back(mod->id());
424  statusOfModules[mod->id()].onPath_ = true;
425  statusOfModules[mod->id()].pathsOn_.push_back(p + offset);
426  }
427  }
428  status.nModules_ = uniqueModules.size() + 1;
429 
430  //add the EndPathStatusInserter at the end
431  auto found = pathStatusInserterModuleLabelToModuleID.find(iPnC.endPaths()[p]);
432  assert(found != pathStatusInserterModuleLabelToModuleID.end());
433  status.modulesOnPath_.push_back(found->second);
434  }
435  }
436 
437  for (auto const& description : iPnC.allModules()) {
438  unsigned int const moduleIndex = description->id();
439  auto const& dependentModules = iPnC.modulesWhoseProductsAreConsumedBy(moduleIndex);
440  auto& deps = statusOfModules[moduleIndex];
441  deps.dependsOn_.reserve(dependentModules.size());
442  for (auto const& depDescription : dependentModules) {
443  if (iPrintDependencies) {
444  edm::LogAbsolute("ModuleDependency")
445  << "ModuleDependency '" << description->moduleLabel() << "' depends on data products from module '"
446  << depDescription->moduleLabel() << "'";
447  }
448  deps.dependsOn_.push_back(depDescription->id());
449  }
450  }
451 
452  unsigned int nPathsFinished = 0;
453 
454  //if a circular dependency exception happens, stackTrace has the info
455  std::vector<unsigned int> stackTrace;
456  bool madeForwardProgress = true;
457  try {
458  //'simulate' the running of the paths. On each step mark each module as 'run'
459  // if all the module's dependencies were fulfilled in a previous step
460  unsigned long long searchIndex = 0;
461  while (madeForwardProgress and nPathsFinished != statusOfPaths.size()) {
462  madeForwardProgress = false;
463  for (auto& p : statusOfPaths) {
464  //the path has already completed in an earlier pass
465  if (p.activeModuleSlot_ == p.nModules_) {
466  continue;
467  }
468  ++searchIndex;
469  bool didRun = checkIfCanRun(searchIndex, p.modulesOnPath_[p.activeModuleSlot_], statusOfModules, stackTrace);
470  if (didRun) {
471  madeForwardProgress = true;
472  ++p.activeModuleSlot_;
473  if (p.activeModuleSlot_ == p.nModules_) {
474  ++nPathsFinished;
475  }
476  }
477  }
478  }
479  } catch (CircularDependencyException const&) {
480  //the last element in stackTrace must appear somewhere earlier in stackTrace
481  std::ostringstream oStr;
482 
483  unsigned int lastIndex = stackTrace.front();
484  bool firstSkipped = false;
485  for (auto id : stackTrace) {
486  if (firstSkipped) {
487  oStr << " module '" << moduleIndexToNames[lastIndex] << "' depends on " << moduleIndexToNames[id] << "\n";
488  } else {
489  firstSkipped = true;
490  }
491  lastIndex = id;
492  }
493  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
494  << "Circular module dependency found in configuration\n"
495  << oStr.str();
496  }
497 
498  auto pathName = [&](PathStatus const& iP) {
499  if (iP.endPath_) {
500  return iPnC.endPaths()[iP.index_];
501  }
502  return iPnC.paths()[iP.index_];
503  };
504 
505  //The program would deadlock
506  if (not madeForwardProgress) {
507  std::ostringstream oStr;
508  auto modIndex = std::numeric_limits<unsigned int>::max();
509  unsigned int presentPath;
510  for (auto itP = statusOfPaths.begin(); itP != statusOfPaths.end(); ++itP) {
511  auto const& p = *itP;
512  if (p.activeModuleSlot_ == p.nModules_) {
513  continue;
514  }
515  //this path is stuck
516  modIndex = p.modulesOnPath_[p.activeModuleSlot_];
517  presentPath = itP - statusOfPaths.begin();
518  break;
519  }
520  //NOTE the following should always be true as at least 1 path should be stuc.
521  // I've added the condition just to be paranoid.
522  if (modIndex != std::numeric_limits<unsigned int>::max()) {
523  struct ProgressInfo {
524  ProgressInfo(unsigned int iMod, unsigned int iPath, bool iPreceeds = false)
525  : moduleIndex_(iMod), pathIndex_(iPath), preceeds_(iPreceeds) {}
526 
527  ProgressInfo(unsigned int iMod) : moduleIndex_(iMod), pathIndex_{}, preceeds_(false) {}
528 
529  unsigned int moduleIndex_ = std::numeric_limits<unsigned int>::max();
530  std::optional<unsigned int> pathIndex_;
531  bool preceeds_;
532 
533  bool operator==(ProgressInfo const& iOther) const {
534  return moduleIndex_ == iOther.moduleIndex_ and pathIndex_ == iOther.pathIndex_;
535  }
536  };
537 
538  std::vector<ProgressInfo> progressTrace;
539  progressTrace.emplace_back(modIndex, presentPath);
540 
541  //The following starts from the first found unrun module on a path. It then finds
542  // the first modules it depends on that was not run. If that module is on a Task
543  // it then repeats the check for that module's dependencies. If that module is on
544  // a path, it checks to see if that module is the first unrun module of a path
545  // and if so it repeats the check for that module's dependencies, if not it
546  // checks the dependencies of the stuck module on that path.
547  // Eventually, all these checks should allow us to find a cycle of modules.
548 
549  //NOTE: the only way foundUnrunModule should ever by false by the end of the
550  // do{}while loop is if there is a bug in the algorithm. I've included it to
551  // try to avoid that case causing an infinite loop in the program.
552  bool foundUnrunModule;
553  do {
554  //check dependencies looking for stuff not run and on a path
555  foundUnrunModule = false;
556  for (auto depMod : statusOfModules[modIndex].dependsOn_) {
557  auto const& depStatus = statusOfModules[depMod];
558  if (not depStatus.wasRun_ and depStatus.onPath_) {
559  foundUnrunModule = true;
560  //last run on a path?
561  bool lastOnPath = false;
562  unsigned int foundPath;
563  for (auto pathOn : depStatus.pathsOn_) {
564  auto const& depPaths = statusOfPaths[pathOn];
565  if (depPaths.modulesOnPath_[depPaths.activeModuleSlot_] == depMod) {
566  lastOnPath = true;
567  foundPath = pathOn;
568  break;
569  }
570  }
571  if (lastOnPath) {
572  modIndex = depMod;
573  progressTrace.emplace_back(modIndex, foundPath);
574  } else {
575  //some earlier module on the same path is stuck
576  progressTrace.emplace_back(depMod, depStatus.pathsOn_[0]);
577  auto const& depPath = statusOfPaths[depStatus.pathsOn_[0]];
578  modIndex = depPath.modulesOnPath_[depPath.activeModuleSlot_];
579  progressTrace.emplace_back(modIndex, depStatus.pathsOn_[0], true);
580  }
581  break;
582  }
583  }
584  if (not foundUnrunModule) {
585  //check unscheduled modules
586  for (auto depMod : statusOfModules[modIndex].dependsOn_) {
587  auto const& depStatus = statusOfModules[depMod];
588  if (not depStatus.wasRun_ and not depStatus.onPath_) {
589  foundUnrunModule = true;
590  progressTrace.emplace_back(depMod);
591  modIndex = depMod;
592  break;
593  }
594  }
595  }
596  } while (foundUnrunModule and (0 == std::count(progressTrace.begin(),
597  progressTrace.begin() + progressTrace.size() - 1,
598  progressTrace.back())));
599 
600  auto printTrace = [&](auto& oStr, auto itBegin, auto itEnd) {
601  for (auto itTrace = itBegin; itTrace != itEnd; ++itTrace) {
602  if (itTrace != itBegin) {
603  if (itTrace->preceeds_) {
604  oStr << " and follows module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' on the path\n";
605  } else {
606  oStr << " and depends on module '" << moduleIndexToNames[itTrace->moduleIndex_] << "'\n";
607  }
608  }
609  if (itTrace + 1 != itEnd) {
610  if (itTrace->pathIndex_) {
611  oStr << " module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' is on path '"
612  << pathName(statusOfPaths[*itTrace->pathIndex_]) << "'";
613  } else {
614  oStr << " module '" << moduleIndexToNames[itTrace->moduleIndex_] << "' is in a task";
615  }
616  }
617  }
618  };
619 
620  if (not foundUnrunModule) {
621  //If we get here, this suggests a problem with either the algorithm that finds problems or the algorithm
622  // that attempts to report the problem
623  oStr << "Algorithm Error, unable to find problem. Contact framework group.\n Traced problem this far\n";
624  printTrace(oStr, progressTrace.begin(), progressTrace.end());
625  } else {
626  printTrace(
627  oStr, std::find(progressTrace.begin(), progressTrace.end(), progressTrace.back()), progressTrace.end());
628  }
629  }
630  //the schedule deadlocked
631  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
632  << "The Path/EndPath configuration could cause the job to deadlock\n"
633  << oStr.str();
634  }
635 
636  //NOTE: although the following conditions are not needed for safe running, they are
637  // policy choices the collaboration has made.
638 
639  //Check to see if for each path if the order of the modules is correct based on dependencies
640  for (auto& p : statusOfPaths) {
641  for (unsigned long int i = 0; p.nModules_ > 0 and i < p.nModules_ - 1; ++i) {
642  auto moduleID = p.modulesOnPath_[i];
643  if (not statusOfModules[moduleID].dependsOn_.empty()) {
644  for (unsigned long int j = i + 1; j < p.nModules_; ++j) {
645  auto testModuleID = p.modulesOnPath_[j];
646  for (auto depModuleID : statusOfModules[moduleID].dependsOn_) {
647  if (depModuleID == testModuleID) {
648  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
649  << "Dependent module later on Path\n"
650  << " module '" << moduleIndexToNames[moduleID] << "' depends on '"
651  << moduleIndexToNames[depModuleID] << "' which is later on path " << pathName(p);
652  }
653  }
654  }
655  }
656  }
657  }
658 
659  //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
660  // as module A then B must appear on ALL paths that have A.
661  unsigned int modIndex = 0;
662  for (auto& mod : statusOfModules) {
663  for (auto& depIndex : mod.dependsOn_) {
664  std::size_t count = 0;
665  std::size_t nonEndPaths = 0;
666  for (auto modPathID : mod.pathsOn_) {
667  if (statusOfPaths[modPathID].endPath_) {
668  continue;
669  }
670  ++nonEndPaths;
671  for (auto depPathID : statusOfModules[depIndex].pathsOn_) {
672  if (depPathID == modPathID) {
673  ++count;
674  break;
675  }
676  }
677  }
678  if (count != 0 and count != nonEndPaths) {
679  std::ostringstream onStr;
680  std::ostringstream missingStr;
681 
682  for (auto modPathID : mod.pathsOn_) {
683  if (statusOfPaths[modPathID].endPath_) {
684  continue;
685  }
686  bool found = false;
687  for (auto depPathID : statusOfModules[depIndex].pathsOn_) {
688  if (depPathID == modPathID) {
689  found = true;
690  }
691  }
692  auto& s = statusOfPaths[modPathID];
693  if (found) {
694  onStr << pathName(s) << " ";
695  } else {
696  missingStr << pathName(s) << " ";
697  }
698  }
699  throw edm::Exception(edm::errors::ScheduleExecutionFailure, "Unrunnable schedule\n")
700  << "Paths are non consistent\n"
701  << " module '" << moduleIndexToNames[modIndex] << "' depends on '" << moduleIndexToNames[depIndex]
702  << "' which appears on paths\n " << onStr.str() << "\nbut is missing from\n " << missingStr.str();
703  }
704  }
705  ++modIndex;
706  }
707  }
708 } // namespace edm
constexpr bool operator==(ELseverityLevel const &e1, ELseverityLevel const &e2) noexcept
std::vector< ModuleDescription const * > const & allModules() const
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
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:1287
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
constexpr auto kInvalidIndex
def move(src, dest)
Definition: eostools.py:511
std::vector< std::string > const & paths() const