CMS 3D CMS Logo

FWFileEntry.cc
Go to the documentation of this file.
1 #include <boost/regex.hpp>
2 
3 #include "TFile.h"
4 #include "TEveTreeTools.h"
5 #include "TError.h"
6 #include "TMath.h"
7 #include "TEnv.h"
8 
14 
17 
23 
25 
27 
28 #include <boost/bind.hpp>
29 
30 FWFileEntry::FWFileEntry(const std::string& name, bool checkVersion) :
31  m_name(name), m_file(nullptr), m_eventTree(nullptr), m_event(nullptr),
32  m_needUpdate(true), m_globalEventList(nullptr)
33 {
34  openFile(checkVersion);
35 }
36 
38 {
39  for(std::list<Filter*>::iterator i = m_filterEntries.begin(); i != m_filterEntries.end(); ++i)
40  delete (*i)->m_eventList;
41 
42  delete m_globalEventList;
43 }
44 
45 void FWFileEntry::openFile(bool checkVersion)
46 {
47  gErrorIgnoreLevel = 3000; // suppress warnings about missing dictionaries
48 
49  TFile *newFile = TFile::Open(m_name.c_str());
50 
51  if (newFile == nullptr || newFile->IsZombie() || !newFile->Get("Events")) {
52  // std::cout << "Invalid file. Ignored." << std::endl;
53  // return false;
54  throw std::runtime_error("Invalid file. Ignored.");
55  }
56 
57  m_file = newFile;
58 
59  gErrorIgnoreLevel = -1;
60 
61  // check CMSSW relese version for compatibility
62  if (checkVersion) {
63  typedef std::vector<edm::ProcessHistory> provList;
64 
65  TTree *metaData = dynamic_cast<TTree*>(m_file->Get("MetaData"));
66  TBranch *b = metaData->GetBranch("ProcessHistory");
67  provList *x = nullptr;
68  b->SetAddress(&x);
69  b->GetEntry(0);
70 
71  const edm::ProcessConfiguration* dd = nullptr;
72  int latestVersion =0;
73  int currentVersionArr[] = {0, 0, 0};
74  for (auto const& processHistory : *x)
75  {
76  for (auto const& processConfiguration : processHistory)
77  {
78  // std::cout << processConfiguration.releaseVersion() << " " << processConfiguration.processName() << std::endl;
79  TString dcv = processConfiguration.releaseVersion();
80  fireworks::getDecomposedVersion(dcv, currentVersionArr);
81  int nvv = currentVersionArr[0]*100 + currentVersionArr[1]*10 + currentVersionArr[2];
82  if (nvv > latestVersion) {
83  latestVersion = nvv;
84  dd = &processConfiguration;
85  }
86  }
87  }
88 
89  if (latestVersion) {
90  fwLog(fwlog::kInfo) << "Checking process history. " << m_name.c_str() << " latest process \"" << dd->processName() << "\", version " << dd->releaseVersion() << std::endl;
91 
92  b->SetAddress(nullptr);
93  TString v = dd->releaseVersion();
95  {
97  TString msg = Form("incompatible data: Process version does not mactch major data formats version. File produced with %s. Data formats version \"CMSSW_%d_%d_%d\".\n",
98  dd->releaseVersion().c_str(), di[0], di[1], di[2]);
99  msg += "Use --no-version-check option if you still want to view the file.\n";
100  throw std::runtime_error(msg.Data());
101  }
102  }
103  else {
104  TString msg = "No process history available\n";
105  msg += "Use --no-version-check option if you still want to view the file.\n";
106  throw std::runtime_error(msg.Data());
107  }
108  }
109 
110  m_eventTree = dynamic_cast<TTree*>(m_file->Get("Events"));
111 
112  if (m_eventTree == nullptr)
113  {
114  throw std::runtime_error("Cannot find TTree 'Events' in the data file");
115  }
116 
117  // Initialize caching, this helps also in the case of local file.
119  printf("FWFileEntry::openFile enabling FWTTreeCache for file class '%s'.", m_file->ClassName());
120 
121  auto tc = new FWTTreeCache(m_eventTree, FWTTreeCache::GetDefaultCacheSize());
122  m_file->SetCacheRead(tc, m_eventTree);
123  gEnv->SetValue("TFile.AsyncReading", 1);
124  tc->SetEnablePrefetching(true);
125  tc->SetLearnEntries(20);
126  tc->SetLearnPrefill(TTreeCache::kAllBranches);
127  tc->StartLearningPhase();
128 
129  // load event, set DataGetterHelper callback for branch access
130  m_event = new fwlite::Event(m_file, false, [tc](TBranch const& b){ tc->BranchAccessCallIn(&b); });
131 
132  // Connect to collection add/remove signals
134  eiMng->newItem_ .connect(boost::bind(&FWFileEntry::NewEventItemCallIn, this, _1));
135  eiMng->removingItem_.connect(boost::bind(&FWFileEntry::RemovingEventItemCallIn, this, _1));
136  // no need to connect to goingToClearItems_ ... individual removes are emitted.
137 
138  if (m_event->size() == 0)
139  throw std::runtime_error("fwlite::Event size == 0");
140 }
141 
143 {
144  if (m_file) {
145  printf("Reading %lld bytes in %d transactions.\n",
146  m_file->GetBytesRead(), m_file->GetReadCalls());
147  delete m_file->GetCacheRead(m_eventTree);
148 
149  m_file->Close();
150  delete m_file;
151  }
152  if (m_event) delete m_event;
153 }
154 
155 //______________________________________________________________________________
156 
157 bool FWFileEntry::isEventSelected(int tree_entry)
158 {
159  int idx = m_globalEventList->GetIndex(tree_entry);
160  return idx >= 0;
161 }
162 
164 {
165  return m_globalEventList->GetN() > 0;
166 }
167 
169 {
170  if (m_globalEventList->GetN() > 0)
171  {
172  return m_globalEventList->GetEntry(0);
173  }
174  else
175  {
176  return -1;
177  }
178 }
179 
181 {
182  if (m_globalEventList->GetN() > 0)
183  return m_globalEventList->GetEntry(m_globalEventList->GetN() - 1);
184  else
185  return -1;
186 }
187 
189 {
190  // Find next selected event after the current one.
191  // This returns the index in the selected event list.
192  // If none exists -1 is returned.
193 
194  const Long64_t *list = m_globalEventList->GetList();
195  Long64_t val = tree_entry;
196  Long64_t idx = TMath::BinarySearch(m_globalEventList->GetN(), list, val);
197  ++idx;
198  if (idx >= m_globalEventList->GetN() || idx < 0)
199  return -1;
200  return list[idx];
201 }
202 
204 {
205  // Find first selected event before current one.
206  // This returns the index in the selected event list.
207  // If none exists -1 is returned.
208 
209  const Long64_t *list = m_globalEventList->GetList();
210  Long64_t val = tree_entry;
211  Long64_t idx = TMath::BinarySearch(m_globalEventList->GetN(), list, val);
212  if (list[idx] == val)
213  --idx;
214  if (idx >= 0)
215  return list[idx];
216  else
217  return -1;
218 }
219 
220 //______________________________________________________________________________
222 {
223  for (std::list<Filter*>::iterator it = m_filterEntries.begin(); it != m_filterEntries.end(); ++it)
224  {
225  if ((*it)->m_selector->m_enabled)
226  return true;
227  }
228 
229  return false;
230 }
231 
232 //______________________________________________________________________________
233 void FWFileEntry::updateFilters(const FWEventItemsManager* eiMng, bool globalOR)
234 {
235  if (!m_needUpdate)
236  return;
237 
238  if (m_globalEventList)
239  m_globalEventList->Reset();
240  else
242 
243  for (std::list<Filter*>::iterator it = m_filterEntries.begin(); it != m_filterEntries.end(); ++it)
244  {
245  if ((*it)->m_selector->m_enabled && (*it)->m_needsUpdate)
246  {
247  runFilter(*it, eiMng);
248  }
249  // Need to re-check if enabled after filtering as it can be set to false
250  // in runFilter().
251  if ((*it)->m_selector->m_enabled)
252  {
253  if ((*it)->hasSelectedEvents())
254  {
255  if (globalOR || m_globalEventList->GetN() == 0)
256  {
257  m_globalEventList->Add((*it)->m_eventList);
258  }
259  else
260  {
261  m_globalEventList->Intersect((*it)->m_eventList);
262  }
263  }
264  else if (!globalOR)
265  {
266  m_globalEventList->Reset();
267  break;
268  }
269  }
270  }
271 
272  fwLog(fwlog::kDebug) << "FWFileEntry::updateFilters in [" << m_file->GetName() << "] global selection [" << m_globalEventList->GetN() << "/" << m_eventTree->GetEntries() << "]" << std::endl;
273 
274  m_needUpdate = false;
275 }
276 
277 //_____________________________________________________________________________
279 {
280  if (!filter->m_selector->m_triggerProcess.empty())
281  {
283  return;
284  }
285 
286  // parse selection for known Fireworks expressions
287  std::string interpretedSelection = filter->m_selector->m_expression;
288  // list of branch names to be added to tree-cache
289  std::vector<std::string> branch_names;
290 
292  end = eiMng->end(); i != end; ++i)
293  {
294  FWEventItem *item = *i;
295  if (item == nullptr)
296  continue;
297  // FIXME: hack to get full branch name filled
298  if (!item->hasEvent())
299  {
300  item->setEvent(m_event);
301  item->getPrimaryData();
302  item->setEvent(nullptr);
303  }
304 
305  boost::regex re(std::string("\\$") + (*i)->name());
306 
307  if (boost::regex_search(interpretedSelection, re))
308  {
309  const edm::TypeWithDict elementType(const_cast<TClass*>(item->type()));
310  const edm::TypeWithDict wrapperType = edm::TypeWithDict::byName(edm::wrappedClassName(elementType.name()));
311  std::string fullBranchName = m_event->getBranchNameFor(wrapperType.typeInfo(),
312  item->moduleLabel().c_str(),
313  item->productInstanceLabel().c_str(),
314  item->processName().c_str());
315 
316  interpretedSelection = boost::regex_replace(interpretedSelection, re,
317  fullBranchName + ".obj");
318 
319  branch_names.push_back(fullBranchName);
320 
321  // printf("selection after applying s/%s/%s/: %s\n",
322  // (std::string("\\$") + (*i)->name()).c_str(),
323  // ((*i)->m_fullBranchName + ".obj").c_str(),
324  // interpretedSelection.c_str());
325  }
326  }
327 
328 
329  std::size_t found = interpretedSelection.find('$');
330  if (found!=std::string::npos)
331  {
332  fwLog(fwlog::kError) << "FWFileEntry::RunFilter invalid expression " << interpretedSelection << std::endl;
333  filter->m_needsUpdate = false;
334  return;
335  }
336 
337  m_file->cd();
338  m_eventTree->SetEventList(nullptr);
339 
340  auto prevCache = m_file->GetCacheRead(m_eventTree);
341 
342  auto interCache = new TTreeCache(m_eventTree, 10*1024*1024);
343  // Do not disconnect the cache, it will be reattached after filtering.
344  m_file->SetCacheRead(interCache, m_eventTree, TFile::kDoNotDisconnect);
345  interCache->SetEnablePrefetching(true);
346  for (auto & b : branch_names)
347  interCache->AddBranch(b.c_str(), true);
348  interCache->StopLearningPhase();
349 
350  // Since ROOT will leave any TBranches used in the filtering at the last event,
351  // we need to be able to reset them to what fwlite::Event expects them to be.
352  // We do this by holding onto the old buffers and create temporary new ones.
353 
354  std::map<TBranch*, void*> prevAddrs;
355 
356  {
357  TObjArray* branches = m_eventTree->GetListOfBranches();
358  std::auto_ptr<TIterator> pIt( branches->MakeIterator());
359  while (TObject* branchObj = pIt->Next())
360  {
361  TBranch* b = dynamic_cast<TBranch*> (branchObj);
362  if (nullptr!=b)
363  {
364  const char * name = b->GetName();
365  unsigned int length = strlen(name);
366  if (length > 1 && name[length-1] != '.')
367  {
368  // This is not a data branch so we should ignore it.
369  continue;
370  }
371  if (nullptr != b->GetAddress())
372  {
373  if (prevAddrs.find(b) != prevAddrs.end())
374  {
375  fwLog(fwlog::kWarning) << "FWFileEntry::runFilter branch is already in the map!\n";
376  }
377  prevAddrs.insert(std::make_pair(b, b->GetAddress()));
378 
379  // std::cout <<"Zeroing branch: "<< b->GetName() <<" "<< (void*) b->GetAddress() <<std::endl;
380  b->SetAddress(nullptr);
381  }
382  }
383  }
384  }
385 
386  if (filter->m_eventList)
387  filter->m_eventList->Reset();
388  else
389  filter->m_eventList = new FWTEventList;
390 
391  fwLog(fwlog::kInfo) << "FWFileEntry::runFilter Running filter " << interpretedSelection << "' "
392  << "for file '" << m_file->GetName() << "'.\n";
393 
394  TEveSelectorToEventList stoelist(filter->m_eventList, interpretedSelection.c_str());
395  Long64_t result = m_eventTree->Process(&stoelist);
396 
397  if (result < 0)
398  fwLog(fwlog::kWarning) << "FWFileEntry::runFilter in file [" << m_file->GetName() << "] filter [" << filter->m_selector->m_expression << "] is invalid." << std::endl;
399  else
400  fwLog(fwlog::kDebug) << "FWFileEntry::runFilter is file [" << m_file->GetName() << "], filter [" << filter->m_selector->m_expression << "] has [" << filter->m_eventList->GetN() << "] events selected" << std::endl;
401 
402  // Set back the old branch buffers.
403  {
404  for (auto i : prevAddrs)
405  {
406  // std::cout <<"Resetting branch: "<< i.first->GetName() <<" "<< i.second <<std::endl;
407  i.first->SetAddress(i.second);
408  }
409  }
410 
411  m_file->SetCacheRead(prevCache, m_eventTree);
412  delete interCache;
413 
414  filter->m_needsUpdate = false;
415 }
416 
417 //______________________________________________________________________________
418 
419 bool
421 {
423 
424  boost::regex re_spaces("\\s+");
425  selection = boost::regex_replace(selection,re_spaces,"");
426  if (selection.find("&&") != std::string::npos &&
427  selection.find("||") != std::string::npos )
428  {
429  // Combination of && and || operators not supported.
430  return false;
431  }
432 
433  fwlite::Handle<edm::TriggerResults> hTriggerResults;
434  edm::TriggerNames const* triggerNames(nullptr);
435  try
436  {
437  hTriggerResults.getByLabel(*m_event,"TriggerResults","", filterEntry->m_selector->m_triggerProcess.c_str());
438  triggerNames = &(m_event->triggerNames(*hTriggerResults));
439  }
440  catch(...)
441  {
442  fwLog(fwlog::kWarning) << " failed to get trigger results with process name "<< filterEntry->m_selector->m_triggerProcess << std::endl;
443  return false;
444  }
445 
446  // std::cout << "Number of trigger names: " << triggerNames->size() << std::endl;
447  // for (unsigned int i=0; i<triggerNames->size(); ++i)
448  // std::cout << " " << triggerNames->triggerName(i);
449  //std::cout << std::endl;
450 
451  bool junction_mode = true; // AND
452  if (selection.find("||")!=std::string::npos)
453  junction_mode = false; // OR
454 
455  boost::regex re("\\&\\&|\\|\\|");
456 
457  boost::sregex_token_iterator i(selection.begin(), selection.end(), re, -1);
458  boost::sregex_token_iterator j;
459 
460  // filters and how they enter in the logical expression
461  std::vector<std::pair<unsigned int,bool> > filters;
462 
463  while (i != j)
464  {
465  std::string filter = *i++;
466  bool flag = true;
467  if (filter[0] == '!')
468  {
469  flag = false;
470  filter.erase(filter.begin());
471  }
472  unsigned int index = triggerNames->triggerIndex(filter);
473  if (index == triggerNames->size())
474  {
475  // Trigger name not found.
476  return false;
477  }
478  filters.push_back(std::make_pair(index, flag));
479  }
480  if (filters.empty())
481  return false;
482 
483  if (filterEntry->m_eventList)
484  filterEntry->m_eventList->Reset();
485  else
486  filterEntry->m_eventList = new FWTEventList();
487  FWTEventList* list = filterEntry->m_eventList;
488 
489  // loop over events
490  edm::EventID currentEvent = m_event->id();
491  unsigned int iEvent = 0;
492 
493  for (m_event->toBegin(); !m_event->atEnd(); ++(*m_event))
494  {
495  hTriggerResults.getByLabel(*m_event,"TriggerResults","", filterEntry->m_selector->m_triggerProcess.c_str());
496  std::vector<std::pair<unsigned int,bool> >::const_iterator filter = filters.begin();
497  bool passed = hTriggerResults->accept(filter->first) == filter->second;
498  while (++filter != filters.end())
499  {
500  if (junction_mode)
501  passed &= hTriggerResults->accept(filter->first) == filter->second;
502  else
503  passed |= hTriggerResults->accept(filter->first) == filter->second;
504  }
505  if (passed)
506  list->Enter(iEvent);
507  ++iEvent;
508  }
509  m_event->to(currentEvent);
510 
511  filterEntry->m_needsUpdate = false;
512 
513  fwLog(fwlog::kDebug) << "FWFile::filterEventsWithCustomParser file [" << m_file->GetName() << "], filter [" << filterEntry->m_selector->m_expression << "], selected [" << list->GetN() << "]" << std::endl;
514 
515  return true;
516 }
517 
518 //------------------------------------------------------------------------------
519 
521 {
522  FWTTreeCache *tc = dynamic_cast<FWTTreeCache*>(m_file->GetCacheRead(m_eventTree));
523  assert(tc != nullptr && "FWFileEntry::treeCache can not access TTreeCache");
524  return tc;
525 }
526 
529 {
530  const edm::TypeWithDict elementType(const_cast<TClass*>(it->type()));
531  const edm::TypeWithDict wrapperType = edm::TypeWithDict::byName(edm::wrappedClassName(elementType.name()));
532  return m_event->getBranchNameFor(wrapperType.typeInfo(),
533  it->moduleLabel().c_str(),
534  it->productInstanceLabel().c_str(),
535  it->processName().c_str());
536 }
537 
539 {
540  auto tc = fwTreeCache();
541 
543  printf("FWFileEntry:NewEventItemCallIn FWEventItem %s, learning=%d\n", getBranchName(it).c_str(),
544  tc->IsLearning());
545 
546  tc->AddBranchTopLevel(getBranchName(it).c_str());
547 }
548 
550 {
551  auto tc = fwTreeCache();
552 
554  printf("FWFileEntry:RemovingEventItemCallIn FWEventItem %s, learning=%d\n", getBranchName(it).c_str(),
555  tc->IsLearning());
556 
557  tc->DropBranchTopLevel(getBranchName(it).c_str());
558 }
fireworks::Context * getContext()
Definition: FWGUIManager.h:155
void closeFile()
Definition: FWFileEntry.cc:142
FWEventSelector * m_selector
Definition: FWFileEntry.h:41
virtual void Enter(Long64_t entry)
Definition: FWTEventList.cc:55
Event const & toBegin() override
Go to the very first Event.
Definition: Event.cc:240
void openFile(bool)
Definition: FWFileEntry.cc:45
int lastSelectedEvent()
Definition: FWFileEntry.cc:180
const FWEventItemsManager * eventItemsManager() const
Definition: Context.h:61
void setEvent(const edm::EventBase *iEvent)
Definition: FWEventItem.cc:122
void RemovingEventItemCallIn(const FWEventItem *it)
Definition: FWFileEntry.cc:549
bool hasSelectedEvents()
Definition: FWFileEntry.cc:163
bool accept() const
Has at least one path accepted the event?
selection
main part
Definition: corrVsCorr.py:98
std::string getBranchName(const FWEventItem *it) const
Definition: FWFileEntry.cc:528
FWTEventList * m_eventList
Definition: FWFileEntry.h:40
void getPrimaryData() const
Definition: FWEventItem.cc:445
std::string m_triggerProcess
FWFileEntry(const std::string &name, bool checkVersion)
Definition: FWFileEntry.cc:30
void getDecomposedVersion(const TString &s, int *out)
Definition: fwPaths.cc:30
Strings::size_type size() const
Definition: TriggerNames.cc:39
const std::string & processName() const
Definition: FWEventItem.cc:531
#define nullptr
std::string const getBranchNameFor(std::type_info const &, char const *iModuleLabel, char const *iProductInstanceLabel, char const *iProcessName) const override
Return the branch name in the TFile which contains the data.
Definition: Event.cc:301
Long64_t size() const
Returns number of events in the file.
Definition: Event.cc:267
bool isEventSelected(int event)
Definition: FWFileEntry.cc:157
static TypeWithDict byName(std::string const &name)
Definition: TypeWithDict.cc:59
std::string const & processName() const
void getByLabel(const P &iP, const char *iModuleLabel, const char *iProductInstanceLabel=0, const char *iProcessLabel=0)
Definition: Handle.h:91
bool atEnd() const override
Definition: Event.cc:283
int iEvent
Definition: GenABIO.cc:230
const std::string & productInstanceLabel() const
Definition: FWEventItem.cc:525
std::list< Filter * > m_filterEntries
Definition: FWFileEntry.h:107
int * supportedDataFormatsVersion()
Definition: fwPaths.cc:43
unsigned int triggerIndex(std::string const &name) const
Definition: TriggerNames.cc:32
std::string name() const
virtual void Add(const TEventList *list)
Definition: FWTEventList.cc:7
static bool IsLogging()
Definition: FWTTreeCache.cc:27
std::string m_expression
const TClass * type() const
Definition: FWEventItem.cc:508
sigc::signal< void, FWEventItem * > newItem_
bool filterEventsWithCustomParser(Filter *filter)
Definition: FWFileEntry.cc:420
bool to(Long64_t iIndex)
Go to the event at index iIndex.
Definition: Event.cc:210
edm::TriggerNames const & triggerNames(edm::TriggerResults const &triggerResults) const override
Definition: Event.cc:425
std::type_info const & typeInfo() const
#define end
Definition: vmac.h:37
std::list< Filter * > & filters()
Definition: FWFileEntry.h:65
static int GetDefaultCacheSize()
Definition: FWTTreeCache.cc:30
TFile * m_file
Definition: FWFileEntry.h:101
virtual ~FWFileEntry()
Definition: FWFileEntry.cc:37
bool m_needUpdate
Definition: FWFileEntry.h:105
TTree * m_eventTree
Definition: FWFileEntry.h:102
fwlite::Event * m_event
Definition: FWFileEntry.h:103
const_iterator begin() const
NOTE: iterator is allowed to return a null object for items that have been removed.
int firstSelectedEvent()
Definition: FWFileEntry.cc:168
static FWGUIManager * getGUIManager()
#define fwLog(_level_)
Definition: fwLog.h:50
ReleaseVersion const & releaseVersion() const
std::string wrappedClassName(std::string const &iFullName)
bool hasEvent() const
Definition: FWEventItem.h:143
void runFilter(Filter *fe, const FWEventItemsManager *eiMng)
Definition: FWFileEntry.cc:278
double b
Definition: hdecay.h:120
gErrorIgnoreLevel
Definition: utils.py:25
int nextSelectedEvent(int event)
Definition: FWFileEntry.cc:188
Definition: Filter.py:1
bool acceptDataFormatsVersion(TString &n)
Definition: fwPaths.cc:71
edm::EventID id() const
Definition: EventBase.h:60
void updateFilters(const FWEventItemsManager *eiMng, bool isOR)
Definition: FWFileEntry.cc:233
FWTTreeCache * fwTreeCache()
Definition: FWFileEntry.cc:520
FWTEventList * m_globalEventList
Definition: FWFileEntry.h:108
bool hasActiveFilters()
Definition: FWFileEntry.cc:221
std::string m_name
Definition: FWFileEntry.h:100
int previousSelectedEvent(int event)
Definition: FWFileEntry.cc:203
std::vector< FWEventItem * >::const_iterator const_iterator
void NewEventItemCallIn(const FWEventItem *it)
Definition: FWFileEntry.cc:538
const std::string & moduleLabel() const
Definition: FWEventItem.cc:520
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run
const_iterator end() const