Skip to content

Commit 6747eef

Browse files
sawenzelclaude
andcommitted
Publish an empty timeframe from the generic tree reader too
This lets the detector publishers built on RootTreeReader follow the same policy as the hand-written reader specs, and guards the consumer that broke once they do. - RootTreeReader published nothing when its tree had no entry, so the EMCAL cells never reached the AOD producer and no AOD was written for a timeframe without collisions. - It now publishes one default-constructed object per branch and then stops. The object is real, so a registered publishing hook still works. - Looping over a tree without entries published nothing forever; it now ends. - SVertexer took vtxRefs.size() - 1 as the number of vertices, which wrapped around once the primary vertex reader sent an empty vector. https://its.cern.ch/jira/browse/O2-7132 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent ce7127e commit 6747eef

2 files changed

Lines changed: 39 additions & 8 deletions

File tree

Detectors/Vertexing/src/SVertexer.cxx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,9 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a
463463

464464
std::unordered_map<GIndex, std::pair<int, int>> tmap;
465465
std::unordered_map<GIndex, bool> rejmap;
466-
int nv = vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them
466+
// The last entry is for unassigned tracks, ignore them. A timeframe holding no collision at
467+
// all has no entry, and the subtraction would then wrap around.
468+
int nv = vtxRefs.size() > 0 ? vtxRefs.size() - 1 : 0;
467469
for (int i = 0; i < 2; i++) {
468470
mTracksPool[i].clear();
469471
mVtxFirstTrack[i].clear();

Framework/Utils/include/DPLUtils/RootTreeReader.h

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,21 @@ class GenericRootTreeReader
305305
context.outputs().snapshot(Output{key.origin, key.description, key.subSpec, std::move(stackcreator())}, object);
306306
};
307307

308+
// A tree can have no entry at all, which is what a timeframe without a single collision
309+
// looks like. Publish a default-constructed object in that case, so that the consumers
310+
// downstream still see the timeframe instead of getting nothing at all. Everything below
311+
// stays the same, including a registered publishing hook, which needs a valid object.
308312
char* data = nullptr;
309-
mBranch->SetAddress(&data);
310-
mBranch->GetEntry(entry);
313+
if (entry >= 0) {
314+
mBranch->SetAddress(&data);
315+
mBranch->GetEntry(entry);
316+
} else {
317+
data = reinterpret_cast<char*>(mClassInfo->New());
318+
if (data == nullptr) {
319+
LOG(error) << "branch " << mName << ": cannot create an empty " << mClassInfo->GetName() << ", nothing published";
320+
return;
321+
}
322+
}
311323

312324
// execute hook if it was registered; if this return true do not proceed further
313325
if (mPublishHook != nullptr && (*mPublishHook).hook(mName, context, Output{mKey.origin, mKey.description, mKey.subSpec, std::move(stackcreator())}, data)) {
@@ -317,8 +329,10 @@ class GenericRootTreeReader
317329
else {
318330
if (mSizeBranch != nullptr) {
319331
size_t datasize = 0;
320-
mSizeBranch->SetAddress(&datasize);
321-
mSizeBranch->GetEntry(entry);
332+
if (entry >= 0) {
333+
mSizeBranch->SetAddress(&datasize);
334+
mSizeBranch->GetEntry(entry);
335+
}
322336
auto* buffer = reinterpret_cast<BinaryDataStoreType*>(data);
323337
if (buffer->size() == datasize) {
324338
LOG(debug) << "branch " << mName << ": publishing binary chunk of " << datasize << " bytes(s)";
@@ -345,7 +359,9 @@ class GenericRootTreeReader
345359
if (delfunc) {
346360
(*delfunc)(data);
347361
}
348-
mBranch->DropBaskets("all");
362+
if (entry >= 0) {
363+
mBranch->DropBaskets("all");
364+
}
349365
}
350366

351367
private:
@@ -412,7 +428,16 @@ class GenericRootTreeReader
412428
/// @return true if data is available
413429
bool next()
414430
{
415-
if ((mReadEntry + 1) >= mNEntries || mNEntries == 0) {
431+
if (mNEntries == 0) {
432+
// The tree has no entry at all. Publish one empty entry and stop, in every publishing
433+
// mode: looping over nothing would never produce anything to publish.
434+
if (mNofPublished >= 0) {
435+
return false;
436+
}
437+
++mNofPublished;
438+
return true;
439+
}
440+
if ((mReadEntry + 1) >= mNEntries) {
416441
if (mPublishingMode == PublishingMode::Single) {
417442
// stop here
418443
if (mReadEntry < mNEntries) {
@@ -458,7 +483,11 @@ class GenericRootTreeReader
458483
bool operator()(ContextType& context,
459484
HeaderTypes&&... headers) const
460485
{
461-
if (mReadEntry >= mNEntries || mNEntries == 0 || (mMaxEntries > 0 && mNofPublished >= mMaxEntries)) {
486+
if (mNEntries == 0) {
487+
if (mNofPublished != 0) { // next() has to have selected the one empty entry
488+
return false;
489+
}
490+
} else if (mReadEntry >= mNEntries || (mMaxEntries > 0 && mNofPublished >= mMaxEntries)) {
462491
return false;
463492
}
464493

0 commit comments

Comments
 (0)