Skip to content

Correct TChain::Add handling of partially loaded chain.#22672

Open
pcanal wants to merge 1 commit into
root-project:masterfrom
pcanal:fix-issue-22671
Open

Correct TChain::Add handling of partially loaded chain.#22672
pcanal wants to merge 1 commit into
root-project:masterfrom
pcanal:fix-issue-22671

Conversation

@pcanal

@pcanal pcanal commented Jun 19, 2026

Copy link
Copy Markdown
Member

This fixes #22671.

@github-actions

Copy link
Copy Markdown

Test Results

    21 files      21 suites   3d 13h 11m 1s ⏱️
 3 871 tests  3 869 ✅ 0 💤 2 ❌
73 643 runs  73 640 ✅ 0 💤 3 ❌

For more details on these failures, see this check.

Results for commit b06c428.

#include "TSystem.h"
#include "TROOT.h"

namespace {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this then is executed as a macro, why this namespace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(the macro is a copy/paste of the user report).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see: great to have such reproducers delivered to us! Perhaps this can be streamlined before integrating.


TRandom3 rng(42);
const std::string base = "chain_add_repro_files";
gSystem->Exec(("rm -rf " + base).c_str());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something xplatform would be better here?

OUTREF execTChainCleanup.ref)

ROOTTEST_ADD_TEST(TChainAddOtherChainGetEntries
MACRO chain_add_getentries.C+)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aclic'd macro works great. I wonder if an executable can be better, making testing a bit faster, of course if no dictionary is required to be generated on the fly.

@vepadulano vepadulano left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix! I have a question and then some suggestions for the test

for (int f = 0; f < nfiles; ++f) {
const std::string fname = dir + "/f_" + std::to_string(f) + ".root";
TFile out(fname.c_str(), "RECREATE");
TTree t("tree", "tree");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just adding here that time and again we've had issues with tests opening a TFile and creating a TTree on the stack. Maybe to avoid headeaches better to do both on the heap.


TChain* make_chain(const std::string& dir, int nfiles)
{
TChain* c = new TChain("tree");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many places in the test with a bare new without a corresponding delete, it would be much better to handle these via unique_ptr

while (a->LoadTree(nav) >= 0) ++nav;
printf(" reachable via LoadTree loop = %lld ← correct total\n", nav);

// Workaround: add the files into a single chain instead of chaining chains.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm not following the logic well, but why is the workaround part of the test?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it is no longer strictly necessary but harmless to do the additional cross check. I will update the comment.

Comment thread tree/tree/src/TChain.cxx
Long64_t nentries = element->GetEntries();
if (fTreeOffset[fNtrees] == TTree::kMaxEntries) {
if (nentries == TTree::kMaxEntries) {
fEntries = TTree::kMaxEntries;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this line completely. fEntries is the member of this chain (the one where Add(TChain *other) is being called). So why are we setting the number of entries to kMaxEntries if the entries of the other chain are not known? Aren't we losing info on the preexisting number of entries in this chain?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not. fEntries == TTree::kMaxEntries indicates that the total number of entries is not known, which is now the case since we don't know the number of element of one of the element of the chain.

@jblomer jblomer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

I think the test should be much simplified and possibly turn into a unit test. The problem can be reproduced, e.g., with a single tree with a single entry that is first added to two chains individually, and then one is merged into the other.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect handling of partially loaded chains in TChain::Add(TChain* chain)

4 participants