Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions roottest/root/tree/reader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ ROOTTEST_ADD_TEST(WarnNoDict
ERRREF WarnNoDict.eref
FIXTURES_REQUIRED root-tree-reader-WarnNoDict-fixture)

ROOTTEST_ADD_TEST(CreateChainNoDictFiles
MACRO CreateChainNoDictFiles.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.

This is mostly a question for @pcanal , I wonder if with this syntax we run the risk of leaving artifacts in the test directory that the reading test might pick up and effectively still read a dictionary from? The ChainNoDict.C test already has a good check at lines 5-6, but what's your opinion?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ACLiC doesn't generate a rootmap here, only CreateChainNoDictFiles_C.so, _C.d and _C_ACLiC_dict_rdict.pcm, so there is nothing that would trigger autoloading of the library when reading. I checked by running ChainNoDict() in the test directory with those files present: after reading both files ChainNoDictHit still has no dictionary and the library isn't loaded.

FIXTURES_SETUP root-tree-reader-ChainNoDict-fixture)

ROOTTEST_ADD_TEST(ChainNoDict
MACRO ChainNoDict.C
FIXTURES_REQUIRED root-tree-reader-ChainNoDict-fixture)

# ROOT-10046
ROOTTEST_ADD_TEST(oddName
COPY_TO_BUILDDIR root-10046.root
Expand Down
38 changes: 38 additions & 0 deletions roottest/root/tree/reader/ChainNoDict.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Read a data member of a split collection of a class without a dictionary
// over a TChain, which re-runs TBranchProxy::Setup() when switching files.
int ChainNoDict()
{
auto c = TClass::GetClass("ChainNoDictHit");
if (c && c->HasDictionary()) {
std::cerr << "Class ChainNoDictHit has a dictionary and it should not." << std::endl;
return 1;
}

TChain chain("t");
chain.Add("chainNoDict0.root");
chain.Add("chainNoDict1.root");
TTreeReader r(&chain);
TTreeReaderArray<double> x(r, "hits.fX");
int nValues = 0;
while (r.Next()) {
const auto entry = r.GetCurrentEntry();
const int file = entry / 10;
if (x.GetSize() != static_cast<std::size_t>(entry % 10)) {
std::cerr << "Wrong size " << x.GetSize() << " in entry " << entry << std::endl;
return 1;
}
for (std::size_t i = 0; i < x.GetSize(); ++i) {
const double expected = 100. * file + entry % 10 + 0.1 * i;
if (std::abs(x[i] - expected) > 1e-9) {
std::cerr << "Wrong value " << x[i] << " in entry " << entry << ", expected " << expected << std::endl;
return 1;
}
++nValues;
}
}
if (nValues != 90) {
std::cerr << "Read " << nValues << " values, expected 90" << std::endl;
return 1;
}
return 0;
}
41 changes: 41 additions & 0 deletions roottest/root/tree/reader/CreateChainNoDictFiles.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef __CreateChainNoDictFiles__
#define __CreateChainNoDictFiles__

#include "TFile.h"
#include "TTree.h"

#include <memory>
#include <string>
#include <vector>

struct ChainNoDictHit {
double fX = 0;
int fId = 0;
};

#ifdef __ROOTCLING__
#pragma link C++ class ChainNoDictHit+;
#pragma link C++ class std::vector<ChainNoDictHit>+;
#endif

// Write two files with a split std::vector of a struct. They are read in
// ChainNoDict.C without this dictionary, so that the collection proxy is emulated.
int CreateChainNoDictFiles()
{
for (int file = 0; file < 2; ++file) {
auto f = std::make_unique<TFile>(("chainNoDict" + std::to_string(file) + ".root").c_str(), "RECREATE");
auto t = std::make_unique<TTree>("t", "test tree");
std::vector<ChainNoDictHit> hits;
t->Branch("hits", &hits, 32000, 99);
for (int entry = 0; entry < 10; ++entry) {
hits.clear();
for (int i = 0; i < entry; ++i)
hits.push_back({100. * file + entry + 0.1 * i, i});
t->Fill();
}
t->Write();
}
return 0;
}

#endif
19 changes: 17 additions & 2 deletions tree/treeplayer/inc/TBranchProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <algorithm>
#include <string>
#include <iostream>
#include <memory>

class TBranch;
class TStreamerElement;
Expand Down Expand Up @@ -106,7 +107,21 @@ namespace Detail {
Long64_t fRead; // Last entry read

void *fWhere; // memory location of the data
TVirtualCollectionProxy *fCollection; // Handle to the collection containing the data chunk.

// Detach the collection proxy from the data before deleting it: an emulated
// collection proxy clears the collection it is attached to on destruction,
// which, e.g. after a TChain switched files, may no longer exist.
struct CollectionDeleter {
void operator()(TVirtualCollectionProxy *collection) const
{
if (!collection)
return;
collection->PopProxy();
delete collection;
}
};
// Shared so that the proxies remain copyable, as the code generated by TTree::MakeProxy copies them
std::shared_ptr<TVirtualCollectionProxy> fCollection; //! Handle to the collection containing the data chunk.

std::size_t fValueSize{}; // Size of the data type of the proxied branch.

Expand Down Expand Up @@ -442,7 +457,7 @@ namespace Detail {
return nullptr;
}

TVirtualCollectionProxy *GetCollection() { return fCollection; }
TVirtualCollectionProxy *GetCollection() { return fCollection.get(); }

// protected:
virtual void *GetStart(UInt_t /*i*/=0) {
Expand Down
26 changes: 11 additions & 15 deletions tree/treeplayer/src/TBranchProxy.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy() :
fClassName(""), fClass(nullptr), fElement(nullptr), fMemberOffset(0), fOffset(0), fArrayLength(1),
fBranch(nullptr), fBranchCount(nullptr),
fNotify(this),
fRead(-1), fWhere(nullptr),fCollection(nullptr)
fRead(-1), fWhere(nullptr)
{
};

Expand All @@ -74,7 +74,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy(TBranchProxyDirector* boss, const char*
fClassName(""), fClass(nullptr), fElement(nullptr), fMemberOffset(0), fOffset(0), fArrayLength(1),
fBranch(nullptr), fBranchCount(nullptr),
fNotify(this),
fRead(-1), fWhere(nullptr),fCollection(nullptr)
fRead(-1), fWhere(nullptr)
{
if (fBranchName.Length() && fBranchName[fBranchName.Length()-1]!='.' && name) {
((TString&)fBranchName).Append(".");
Expand All @@ -92,7 +92,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy(TBranchProxyDirector* boss, const char
fClassName(""), fClass(nullptr), fElement(nullptr), fMemberOffset(0), fOffset(0), fArrayLength(1),
fBranch(nullptr), fBranchCount(nullptr),
fNotify(this),
fRead(-1), fWhere(nullptr),fCollection(nullptr)
fRead(-1), fWhere(nullptr)
{
if (name && strlen(name)) {
if (fBranchName.Length() && fBranchName[fBranchName.Length()-1]!='.') {
Expand All @@ -113,7 +113,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy(TBranchProxyDirector* boss, Detail::TBr
fClassName(""), fClass(nullptr), fElement(nullptr), fMemberOffset(0), fOffset(0), fArrayLength(1),
fBranch(nullptr), fBranchCount(nullptr),
fNotify(this),
fRead(-1), fWhere(nullptr),fCollection(nullptr)
fRead(-1), fWhere(nullptr)
{
if (name && strlen(name)) {
if (fBranchName.Length() && fBranchName[fBranchName.Length()-1]!='.') {
Expand All @@ -133,7 +133,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy(TBranchProxyDirector* boss, TBranch* br
fClassName(""), fClass(nullptr), fElement(nullptr), fMemberOffset(0), fOffset(0), fArrayLength(1),
fBranch(nullptr), fBranchCount(nullptr),
fNotify(this),
fRead(-1), fWhere(nullptr),fCollection(nullptr)
fRead(-1), fWhere(nullptr)
{
boss->Attach(this);
}
Expand Down Expand Up @@ -186,8 +186,7 @@ ROOT::Detail::TBranchProxy::TBranchProxy(TBranchProxyDirector *boss, const char
fBranchCount(nullptr),
fNotify(this),
fRead(-1),
fWhere(nullptr),
fCollection(nullptr)
fWhere(nullptr)
{
// Constructor.

Expand Down Expand Up @@ -220,8 +219,7 @@ void ROOT::Detail::TBranchProxy::Reset()
fIsClone = false;
fInitialized = false;
fHasLeafCount = false;
delete fCollection;
fCollection = nullptr;
fCollection.reset();
}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -277,8 +275,7 @@ bool ROOT::Detail::TBranchProxy::Setup()
} else if (pcl->GetCollectionProxy()) {
// We always skip the collections.

if (fCollection) delete fCollection;
fCollection = pcl->GetCollectionProxy()->Generate();
fCollection.reset(pcl->GetCollectionProxy()->Generate(), CollectionDeleter{});
pcl = fCollection->GetValueClass();
if (pcl == nullptr) {
// coverity[dereference] fparent is checked jus a bit earlier and can not be null here
Expand Down Expand Up @@ -414,8 +411,7 @@ bool ROOT::Detail::TBranchProxy::Setup()
if (!fIsMember) fIsClone = true;
fClass = clones->GetClass();
} else if (fClass && fClass->GetCollectionProxy()) {
delete fCollection;
fCollection = fClass->GetCollectionProxy()->Generate();
fCollection.reset(fClass->GetCollectionProxy()->Generate(), CollectionDeleter{});
fClass = fCollection->GetValueClass();
}

Expand All @@ -437,7 +433,7 @@ bool ROOT::Detail::TBranchProxy::Setup()
} else if (be->GetType()==4) {
// top level TClonesArray

fCollection = be->GetCollectionProxy()->Generate();
fCollection.reset(be->GetCollectionProxy()->Generate(), CollectionDeleter{});
fIsaPointer = false;
fWhere = be->GetObject();

Expand All @@ -449,7 +445,7 @@ bool ROOT::Detail::TBranchProxy::Setup()

} else if (be->GetType()==41) {

fCollection = be->GetCollectionProxy()->Generate();
fCollection.reset(be->GetCollectionProxy()->Generate(), CollectionDeleter{});
fWhere = be->GetObject();
fOffset += be->GetOffset();

Expand Down
Loading