From 5beaad5b222b1035b333e0982b170906efb38098 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 24 Aug 2026 11:44:34 +0200 Subject: [PATCH 1/2] Let the digit readers accept a timeframe without collisions This fixes a crash in the ITS, MFT, MCH and MID digit readers when the digit tree of a timeframe has no entry. - A timeframe holds no collision at all whenever the interaction rate is low enough, and the digitiser then writes a valid tree with zero entries. - The ITS/MFT reader guarded this with an assert, which is compiled out of every production build because ENABLE_CASSERT defaults to OFF, and then dereferenced branch addresses that GetEntry had not filled. - The MCH and MID readers threw on the failed TTreeReader::Next(). - All four now send empty output and end the stream. - The two asserts in the ITS/MFT connectTree become real errors for the same reason. https://its.cern.ch/jira/browse/O2-7132 Co-Authored-By: Claude Opus 5 --- .../common/workflow/src/DigitReaderSpec.cxx | 36 +++++++++++++++++-- Detectors/MUON/MCH/IO/src/DigitReaderSpec.cxx | 15 ++++++++ .../MUON/MID/Workflow/src/DigitReaderSpec.cxx | 15 ++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index b6c3ab5386179..e9db31ec3222e 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx @@ -26,6 +26,7 @@ #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsITSMFT/PhysTrigger.h" #include "CommonUtils/NameConf.h" #include "CommonDataFormat/IRFrame.h" @@ -101,7 +102,32 @@ void DigitReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry(); if (!mUseIRFrames) { ent++; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and the + // digit tree then has no entry to read. Send empty output rather than dereferencing the + // branch addresses, which GetEntry has not filled. (This used to be an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF.) + LOG(info) << mDetName << "DigitReader has no entry to read, sending empty output"; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, std::vector{}); + pc.outputs().snapshot(Output{Origin, "DIGITS", iLayer}, std::vector{}); + if (mUseMC) { + auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", iLayer}); + o2::dataformats::MCTruthContainer noLabels; + noLabels.flatten_to(sharedlabels); + pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, std::vector{}); + } + } + if (mUseCalib) { + pc.outputs().snapshot(Output{Origin, "GBTCALIB", 0}, std::vector{}); + } + if (mTriggerOut) { + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, std::vector{}); + } + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { LOG(info) << mDetName << "DigitReader" << ((mDoStaggering) ? std::format(": {}", iLayer) : "") << " pushes " << mDigROFRec[iLayer]->size() << " ROFRecords, " << mDigits[iLayer]->size() << " digits at entry " << ent; @@ -215,9 +241,13 @@ void DigitReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); - assert(mFile && !mFile->IsZombie()); + if (!mFile || mFile->IsZombie()) { + throw std::runtime_error(std::format("Cannot open {}", filename)); + } mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); - assert(mTree); + if (!mTree) { + throw std::runtime_error(std::format("Tree {} not found in {}", mDigTreeName, filename)); + } for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { setBranchAddress(mDigitROFBranchName, mDigROFRec[iLayer], iLayer); setBranchAddress(mDigitBranchName, mDigits[iLayer], iLayer); diff --git a/Detectors/MUON/MCH/IO/src/DigitReaderSpec.cxx b/Detectors/MUON/MCH/IO/src/DigitReaderSpec.cxx index 78a0022e07166..af13460a42dd0 100644 --- a/Detectors/MUON/MCH/IO/src/DigitReaderSpec.cxx +++ b/Detectors/MUON/MCH/IO/src/DigitReaderSpec.cxx @@ -37,6 +37,7 @@ #include "DataFormatsMCH/ROFRecord.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" +#include "Framework/Logger.h" #include "Framework/DataSpecUtils.h" #include "Framework/Task.h" #include "Framework/WorkflowSpec.h" @@ -109,6 +110,20 @@ class DigitsReaderDeviceDPL void sendNextTF(ProcessingContext& pc) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and the + // digit tree then has no entry. Send empty containers and finish, rather than throwing. + if (mTreeReader.GetEntries() == 0) { + LOG(info) << "digit tree has no entry, sending empty output"; + pc.outputs().snapshot(OutputRef{"rofs"}, std::vector{}); + pc.outputs().snapshot(OutputRef{"digits"}, std::vector{}); + if (mUseMC) { + pc.outputs().snapshot(OutputRef{"labels"}, dataformats::MCTruthContainer{}); + } + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } + // load the next TF and check its validity (missing branch, ...) if (!mTreeReader.Next()) { throw std::invalid_argument(mTreeReader.fgEntryStatusText[mTreeReader.GetEntryStatus()]); diff --git a/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx b/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx index f65415b8d701a..0479452bcd6f5 100644 --- a/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/DigitReaderSpec.cxx @@ -28,6 +28,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" +#include "Framework/Logger.h" #include "Framework/DataSpecUtils.h" #include "Framework/Task.h" #include "Framework/WorkflowSpec.h" @@ -103,6 +104,20 @@ class DigitsReaderDeviceDPL void sendNextTF(ProcessingContext& pc) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and the + // digit tree then has no entry. Send empty containers and finish, rather than throwing. + if (mTreeReader.GetEntries() == 0) { + LOG(info) << "digit tree has no entry, sending empty output"; + pc.outputs().snapshot(OutputRef{"rofs"}, std::vector{}); + pc.outputs().snapshot(OutputRef{"digits"}, std::vector{}); + if (mUseMC) { + pc.outputs().snapshot(OutputRef{"labels"}, dataformats::MCTruthContainer{}); + } + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } + // load the next TF and check its validity (missing branch, ...) if (!mTreeReader.Next()) { throw std::invalid_argument(mTreeReader.fgEntryStatusText[mTreeReader.GetEntryStatus()]); From dc8f978ec98295bcff3b07bc962e0472bae47d12 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 24 Aug 2026 11:44:34 +0200 Subject: [PATCH 2/2] Check for the end of the tree where the reader specs asserted it This replaces a disabled assert with a real check in 36 ROOT-tree reader specs, so that a timeframe whose tree has no entry ends the stream instead of reading past the end. - Every one of them carried the same two lines: assert(ent < mTree->GetEntries()) with the comment "this should not happen", followed by mTree->GetEntry(ent). - ENABLE_CASSERT defaults to OFF, so the assert is compiled out of every production build and the reader then publishes branch addresses that GetEntry never filled. - A timeframe holds no collision whenever the interaction rate is low enough, which is when the trees come out empty. - The readers now end the stream, which the consumers downstream already handle. - Detectors/Upgrades/ALICE3/IOTOF is left alone: it has no ControlService. https://its.cern.ch/jira/browse/O2-7132 Co-Authored-By: Claude Opus 5 --- Detectors/CPV/workflow/src/ClusterReaderSpec.cxx | 11 ++++++++++- Detectors/CPV/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx | 11 ++++++++++- Detectors/Filtering/src/FilteredTFReaderSpec.cxx | 11 ++++++++++- .../readers/src/GlobalFwdTrackReaderSpec.cxx | 11 ++++++++++- .../readers/src/IRFrameReaderSpec.cxx | 11 ++++++++++- .../readers/src/MatchedMCHMIDReaderSpec.cxx | 11 ++++++++++- .../readers/src/MatchedMFTMCHReaderSpec.cxx | 11 ++++++++++- .../readers/src/PrimaryVertexReaderSpec.cxx | 11 ++++++++++- .../readers/src/SecondaryVertexReaderSpec.cxx | 11 ++++++++++- .../readers/src/StrangenessTrackingReaderSpec.cxx | 11 ++++++++++- .../readers/src/TrackCosmicsReaderSpec.cxx | 11 ++++++++++- .../readers/src/TrackTPCITSReaderSpec.cxx | 11 ++++++++++- Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx | 11 ++++++++++- Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx | 11 ++++++++++- Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx | 11 ++++++++++- .../ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx | 11 ++++++++++- Detectors/ITSMFT/MFT/workflow/src/TrackReaderSpec.cxx | 11 ++++++++++- .../ITSMFT/common/workflow/src/ClusterReaderSpec.cxx | 11 ++++++++++- Detectors/PHOS/workflow/src/CellReaderSpec.cxx | 11 ++++++++++- Detectors/PHOS/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/TOF/workflowIO/src/CalibClusReaderSpec.cxx | 11 ++++++++++- Detectors/TOF/workflowIO/src/ClusterReaderSpec.cxx | 11 ++++++++++- .../TPC/workflow/readers/src/TrackReaderSpec.cxx | 11 ++++++++++- Detectors/TRD/workflow/io/src/TRDTrackReaderSpec.cxx | 11 ++++++++++- .../TRKFT3/common/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- .../Upgrades/ITS3/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/ZDC/workflow/src/DigitReaderSpec.cxx | 11 ++++++++++- Detectors/ZDC/workflow/src/RecEventReaderSpec.cxx | 11 ++++++++++- Detectors/ZDC/workflow/src/RecoReaderSpec.cxx | 11 ++++++++++- 36 files changed, 360 insertions(+), 36 deletions(-) diff --git a/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx index f9d0817325c36..dbcbd8a73510e 100644 --- a/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx @@ -41,7 +41,16 @@ void ClusterReader::init(InitContext& ic) void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mClusters.size() << " Clusters in " << mTRs.size() << " TriggerRecords at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "CLUSTERS", 0}, mClusters); diff --git a/Detectors/CPV/workflow/src/DigitReaderSpec.cxx b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx index 20fe497eb5d0c..29e88a8371319 100644 --- a/Detectors/CPV/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx @@ -41,7 +41,16 @@ void DigitReader::init(InitContext& ic) void DigitReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); diff --git a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx index 81e6f53f42dcc..2e49eb08ed7cf 100644 --- a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx @@ -86,7 +86,16 @@ void DigitReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry(); if (!mUseIRFrames) { ent++; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, mDigits); diff --git a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx index 628a2160c6d0c..08da00dd83051 100644 --- a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx @@ -77,7 +77,16 @@ void DigitReader::run(ProcessingContext& pc) } } auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "FDD DigitReader pushes " << digitsBC->size() << " digits"; diff --git a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx index 3c4812c75b251..a8e78acd487d7 100644 --- a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx @@ -45,7 +45,16 @@ void RecPointReader::init(InitContext& ic) void RecPointReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "FDD RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; diff --git a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx index 09586d778ac15..3f9e5c75b1aae 100644 --- a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx @@ -61,7 +61,16 @@ void DigitReader::run(ProcessingContext& pc) mTree->SetBranchAddress("FT0DIGITSMCTR", &plabels); } auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "FT0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0}, digits); diff --git a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx index ba5ae4aa1356c..f5404c23dfbd5 100644 --- a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx @@ -45,7 +45,16 @@ void RecPointReader::init(InitContext& ic) void RecPointReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "FT0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; diff --git a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx index a49bda2cec18b..491f98771f51b 100644 --- a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx @@ -62,7 +62,16 @@ void DigitReader::run(ProcessingContext& pc) mTree->SetBranchAddress("FV0DigitLabels", &plabels); } auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "FV0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0}, digits); diff --git a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx index 5997cac500ee6..ecf4796353c1c 100644 --- a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx @@ -45,7 +45,16 @@ void RecPointReader::init(InitContext& ic) void RecPointReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "FV0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; diff --git a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx index 22fe1370040db..0e2920532add8 100644 --- a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx +++ b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx @@ -40,7 +40,16 @@ void FilteredTFReader::run(ProcessingContext& pc) // FIXME: fill all output headers by TF specific info (extend findMessageHeaderStack) auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing filtered TF: " << mFiltTF.header.asString(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx index 11fa58333f89b..465552259561d 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx @@ -61,7 +61,16 @@ void GlobalFwdTrackReader::init(InitContext& ic) void GlobalFwdTrackReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Global Forward tracks at entry " << ent; diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx index c1810a1deb743..e63c3ca327164 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx @@ -60,7 +60,16 @@ void IRFrameReaderSpec::init(InitContext& ic) void IRFrameReaderSpec::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "Pushing " << mIRF.size() << " IR-frames in at entry " << ent; pc.outputs().snapshot(Output{mDataOrigin, "IRFRAMES", mSubSpec}, mIRF); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMCHMIDReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMCHMIDReaderSpec.cxx index dc8cf71575787..a8e48f156181d 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMCHMIDReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMCHMIDReaderSpec.cxx @@ -61,7 +61,16 @@ void MatchMCHMIDReader::init(InitContext& ic) void MatchMCHMIDReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " MCHMID matches at entry " << ent; diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx index 5f02beebd1746..1e3c1015427f9 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx @@ -61,7 +61,16 @@ void MatchMFTMCHReader::init(InitContext& ic) void MatchMFTMCHReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " MFTMCH matches at entry " << ent; diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx index 6e1aba8b2e1f3..0182c01f8d3f6 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx @@ -80,7 +80,16 @@ void PrimaryVertexReader::init(InitContext& ic) void PrimaryVertexReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mVerticesPtr->size() << " vertices at entry " << ent; diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx index 9f252616c9d55..30476f20b4493 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx @@ -89,7 +89,16 @@ void SecondaryVertexReader::init(InitContext& ic) void SecondaryVertexReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOGP(info, "Pushing {} V0s ({} indices), {} cascades ({} indices) and {} 3-body ({} indices ) at entry {}", mV0s.size(), mV0sIdx.size(), mCascs.size(), mCascsIdx.size(), m3Bodys.size(), m3BodysIdx.size(), ent); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx index 8c7f87a720925..0ea73c1dc0dba 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx @@ -73,7 +73,16 @@ void StrangenessTrackingReader::init(InitContext& ic) void StrangenessTrackingReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mStrangeTrack.size() << " strange tracks at entry " << ent; pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0}, mStrangeTrack); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx index 7e3cdffd84a6d..e11cfa719f733 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx @@ -37,7 +37,16 @@ void TrackCosmicsReader::init(InitContext& ic) void TrackCosmicsReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Cosmic Tracks at entry " << ent; diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx index c7fd0d543ecf6..8d1c3dbc1e039 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx @@ -64,7 +64,16 @@ void TrackTPCITSReader::init(InitContext& ic) void TrackTPCITSReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " TPC-ITS matches at entry " << ent; diff --git a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx index 9ac5074acb505..5fef823ae1138 100644 --- a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx @@ -68,7 +68,16 @@ void ClusterReaderTask::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); pc.outputs().snapshot(Output{"HMP", "CLUSTERS", 0}, mClustersFromFile); diff --git a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx index 88f6df2bce2e7..df7910580558a 100644 --- a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx @@ -112,7 +112,16 @@ void DigitReader::run(ProcessingContext& pc) // mTree->Print("toponly"); auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); pc.outputs().snapshot(Output{"HMP", "DIGITS", 0}, mDigitsFromFile); diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx index 2f081a11c28b9..1f7677d66c784 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx @@ -34,7 +34,16 @@ void TrackReader::init(InitContext& ic) void TrackReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " track at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "ITSTrackROF", 0}, mROFRec); diff --git a/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx index e92f08af23c0d..d70f2e64e6970 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/VertexReaderSpec.cxx @@ -37,7 +37,16 @@ void VertexReader::init(InitContext& ic) void VertexReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mVerticesPtr->size() << " vertices in " << mVerticesROFRecPtr->size() << " ROFs at entry " << ent; diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackReaderSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackReaderSpec.cxx index 1a2ae573af536..3d0068febc6c2 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackReaderSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackReaderSpec.cxx @@ -42,7 +42,16 @@ void TrackReader::init(InitContext& ic) void TrackReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " track in " << mROFRec.size() << " ROFs at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "MFTTrackROF", 0}, mROFRec); diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index 6174938171336..efe9376fb7d0a 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -57,7 +57,16 @@ template void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { diff --git a/Detectors/PHOS/workflow/src/CellReaderSpec.cxx b/Detectors/PHOS/workflow/src/CellReaderSpec.cxx index c7d93fc20301f..aa7f5282679bd 100644 --- a/Detectors/PHOS/workflow/src/CellReaderSpec.cxx +++ b/Detectors/PHOS/workflow/src/CellReaderSpec.cxx @@ -41,7 +41,16 @@ void CellReader::init(InitContext& ic) void CellReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mCells.size() << " Cells in " << mTRs.size() << " TriggerRecords at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "CELLS", 0}, mCells); diff --git a/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx b/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx index 70f5077b2f0c9..3df5b9a4d02e9 100644 --- a/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx @@ -41,7 +41,16 @@ void DigitReader::init(InitContext& ic) void DigitReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); diff --git a/Detectors/TOF/workflowIO/src/CalibClusReaderSpec.cxx b/Detectors/TOF/workflowIO/src/CalibClusReaderSpec.cxx index 116f93a06c208..983dd59b33699 100644 --- a/Detectors/TOF/workflowIO/src/CalibClusReaderSpec.cxx +++ b/Detectors/TOF/workflowIO/src/CalibClusReaderSpec.cxx @@ -36,7 +36,16 @@ void CalibClusReader::init(InitContext& ic) void CalibClusReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "Pushing " << mPclusInfos->size() << " TOF clusters calib info at entry " << ent; pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "INFOCALCLUS", 0}, mClusInfos); diff --git a/Detectors/TOF/workflowIO/src/ClusterReaderSpec.cxx b/Detectors/TOF/workflowIO/src/ClusterReaderSpec.cxx index e2979a8fc0dbf..35ae8b4fa2851 100644 --- a/Detectors/TOF/workflowIO/src/ClusterReaderSpec.cxx +++ b/Detectors/TOF/workflowIO/src/ClusterReaderSpec.cxx @@ -40,7 +40,16 @@ void ClusterReader::init(InitContext& ic) void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(debug) << "Pushing " << mClustersPtr->size() << " TOF clusters at entry " << ent; diff --git a/Detectors/TPC/workflow/readers/src/TrackReaderSpec.cxx b/Detectors/TPC/workflow/readers/src/TrackReaderSpec.cxx index d73da0cb0d33c..adc4ac4698634 100644 --- a/Detectors/TPC/workflow/readers/src/TrackReaderSpec.cxx +++ b/Detectors/TPC/workflow/readers/src/TrackReaderSpec.cxx @@ -42,7 +42,16 @@ void TrackReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; accumulate(ent, 1); // to really accumulate all, use accumulate(ent,mTree->GetEntries()); - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); using TrackTunePar = o2::globaltracking::TrackTuneParams; const auto& trackTune = TrackTunePar::Instance(); diff --git a/Detectors/TRD/workflow/io/src/TRDTrackReaderSpec.cxx b/Detectors/TRD/workflow/io/src/TRDTrackReaderSpec.cxx index cd9702a3d2385..75dd29b5d4645 100644 --- a/Detectors/TRD/workflow/io/src/TRDTrackReaderSpec.cxx +++ b/Detectors/TRD/workflow/io/src/TRDTrackReaderSpec.cxx @@ -38,7 +38,16 @@ void TRDTrackReader::init(InitContext& ic) void TRDTrackReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " tracks and " << mTrigRec.size() << " trigger records at entry " << ent; if (mUseMC) { diff --git a/Detectors/Upgrades/ALICE3/TRKFT3/common/workflow/src/DigitReaderSpec.cxx b/Detectors/Upgrades/ALICE3/TRKFT3/common/workflow/src/DigitReaderSpec.cxx index ec2b6d4d66192..8a1f461ca8f26 100644 --- a/Detectors/Upgrades/ALICE3/TRKFT3/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRKFT3/common/workflow/src/DigitReaderSpec.cxx @@ -60,7 +60,16 @@ void DigitReader::init(InitContext& ic) void DigitReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); for (int iLayer = 0; iLayer < mLayers; ++iLayer) { diff --git a/Detectors/Upgrades/ITS3/workflow/src/DigitReaderSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/DigitReaderSpec.cxx index 141457c319b9b..75038b2bb6440 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/DigitReaderSpec.cxx @@ -47,7 +47,16 @@ void ITS3DigitReader::init(InitContext& ic) void ITS3DigitReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); for (uint32_t iLayer = 0; iLayer < (mDoStaggering ? NLayers : 1); ++iLayer) { diff --git a/Detectors/ZDC/workflow/src/DigitReaderSpec.cxx b/Detectors/ZDC/workflow/src/DigitReaderSpec.cxx index e952111e0c6c3..adc115030b2e9 100644 --- a/Detectors/ZDC/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ZDC/workflow/src/DigitReaderSpec.cxx @@ -66,7 +66,16 @@ void DigitReader::run(ProcessingContext& pc) } auto ent = mTree->GetReadEntry() < 0 ? mTree->GetReadEntry() + mFirstEntry + 1 : mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "ZDCDigitReader pushed " << zdcOrbitData.size() << " orbits with " << zdcBCData.size() << " bcs and " << zdcChData.size() << " digits"; pc.outputs().snapshot(Output{"ZDC", "DIGITSPD", 0}, zdcOrbitData); diff --git a/Detectors/ZDC/workflow/src/RecEventReaderSpec.cxx b/Detectors/ZDC/workflow/src/RecEventReaderSpec.cxx index 18c620e427569..c068209037893 100644 --- a/Detectors/ZDC/workflow/src/RecEventReaderSpec.cxx +++ b/Detectors/ZDC/workflow/src/RecEventReaderSpec.cxx @@ -45,7 +45,16 @@ void RecEventReader::init(InitContext& ic) void RecEventReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "ZDC RecEventReader pushes " << mBCRecData->size() << " events with " << mBCRecData->size() << " energy, " << mZDCTDCData->size() << " TDC and " << mZDCInfo->size() << " info records at entry " << ent; diff --git a/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx b/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx index 33b2b59d8247b..672191e2a5e19 100644 --- a/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx +++ b/Detectors/ZDC/workflow/src/RecoReaderSpec.cxx @@ -64,7 +64,16 @@ void RecoReader::run(ProcessingContext& pc) mTree->SetBranchAddress("ZDCWaveform", &WaveformDataPtr); auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen + if (ent >= mTree->GetEntries()) { + // A timeframe holds no collision at all whenever the interaction rate is low enough, and + // the tree then has no entry to read. End the stream instead of reading past the end and + // publishing branch addresses that GetEntry has not filled. This was an assert, which is + // compiled out of every production build since ENABLE_CASSERT defaults to OFF. + LOG(info) << "no entry to read, ending the stream"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } mTree->GetEntry(ent); LOG(info) << "ZDCRecoReader pushed " << RecBC.size() << " b.c. " << Energy.size() << " Energies " << TDCData.size() << " TDCs " << Info.size() << " Infos " << WaveformData.size() << " Waveform chunks"; pc.outputs().snapshot(Output{"ZDC", "BCREC", 0}, RecBC);