Skip to content

Commit b0868f3

Browse files
committed
[tree] Improve logic to refresh branch addresses of TChain friends
Part of the logic of TChain::LoadTree deals with updating the status of friend trees. First, the current entry is loaded on all friends. In case the friend tree is a chain that needs to switch to a new tree, the branch addresses of the friend are also updated. This logic only considered the case of top-level friends of a TChain. Notably, it did not consider the case of a main TChain where one (or more) of the trees in the chain have themselves some friends, but not the main chain itself: TChain c1 --> TTree t1 <friendswith> TChain c2 This commit condenses the logic that updates branch addresses of friends in a separate method of TChain, namely RefreshFriendAddresses. The new method is called in TChain::LoadTree when either the chain itself or the current tree have friends. Part of the new logic relies on an addition to TFriendElement. In particular, when the current tree of the chain is loading a new entry and this results in loading the entry of one of its friends which may have to switch to a new tree, the friend calls MarkUpdated to let the tree know it was updated. In case the tree belongs to the chain, it can't know a priori which branches are requested by the user (an information stored in the fStatus data member of TChain). Thus, TFriendElement now has two bits that represent the updated status. The first is the preexisting kUpdated, the second is the new kUpdatedForChain. A call to MarkUpdated sets both bits. In TTree::LoadTree, only the preexisting bit is checked and reset. The new bit is instead checked and reset only in TChain::RefreshFriendAddresses. A new test is added with a main TChain that has both a top-level TChain friend and where its only tree is also connected to another friend TChain.
1 parent 59682a4 commit b0868f3

File tree

5 files changed

+315
-78
lines changed

5 files changed

+315
-78
lines changed

tree/tree/inc/TChain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class TChain : public TTree {
4949
TChain& operator=(const TChain&); // not implemented
5050
void
5151
ParseTreeFilename(const char *name, TString &filename, TString &treename, TString &query, TString &suffix) const;
52+
Long64_t RefreshFriendAddresses();
5253

5354
protected:
5455
void InvalidateCurrentTree();

tree/tree/inc/TFriendElement.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class TFriendElement : public TNamed {
4747
public:
4848
enum EStatusBits {
4949
kFromChain = BIT(9), // Indicates a TChain inserted this element in one of its content TTree
50-
kUpdated = BIT(10) // Indicates that the chain 'fTree' went through a LoadTree
50+
kUpdated = BIT(10), // Indicates that the chain 'fTree' went through a LoadTree
51+
kUpdatedForChain = BIT(11), // Same as kUpdated, but detected only by the chain itself and not the current tree
5152
};
5253
TFriendElement();
5354
TFriendElement(TTree *tree, const char *treename, const char *filename);
@@ -65,8 +66,10 @@ class TFriendElement : public TNamed {
6566
void ls(Option_t *option="") const override;
6667
void Reset() { fTree = nullptr; fFile = nullptr; }
6768
bool IsUpdated() const { return TestBit(kUpdated); }
69+
bool IsUpdatedForChain() const { return TestBit(kUpdatedForChain); }
6870
void ResetUpdated() { ResetBit(kUpdated); }
69-
void MarkUpdated() { SetBit(kUpdated); }
71+
void ResetUpdatedForChain() { ResetBit(kUpdatedForChain); }
72+
void MarkUpdated() { SetBit(kUpdated); SetBit(kUpdatedForChain); }
7073
void RecursiveRemove(TObject *obj) override;
7174

7275

tree/tree/src/TChain.cxx

Lines changed: 99 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,88 @@ Int_t TChain::LoadBaskets(Long64_t /*maxmemory*/)
12311231
return 0;
12321232
}
12331233

1234+
////////////////////////////////////////////////////////////////////////////////
1235+
/// Refresh branch/leaf addresses of friend trees
1236+
///
1237+
/// The method acts only on the current tree in the chain (fTree), but it may
1238+
/// be called in two different scenarios: when there are friends of the chain
1239+
/// or when there are friends of fTree itself.
1240+
Long64_t TChain::RefreshFriendAddresses()
1241+
{
1242+
assert(fTree != nullptr);
1243+
1244+
bool needUpdate = false;
1245+
if (auto *innerFriendList = fTree->GetListOfFriends()) {
1246+
// If the current tree has friends, check if they were mark for update
1247+
// when switching to the following tree, detect it so that we later we
1248+
// actually refresh the addresses of the friends.
1249+
for (auto *frEl : ROOT::Detail::TRangeStaticCast<TFriendElement>(*innerFriendList)) {
1250+
if (frEl->IsUpdated()) {
1251+
needUpdate = true;
1252+
frEl->ResetUpdated();
1253+
}
1254+
if (frEl->IsUpdatedForChain()) {
1255+
needUpdate = true;
1256+
frEl->ResetUpdatedForChain();
1257+
}
1258+
}
1259+
}
1260+
1261+
if (!needUpdate)
1262+
return 0;
1263+
1264+
// Update the branch/leaf addresses and the list of leaves in all
1265+
// TTreeFormula of the TTreePlayer (if any).
1266+
for (auto *chainEl : ROOT::Detail::TRangeStaticCast<TChainElement>(*fStatus)) {
1267+
// Set the branch status of all the chain elements, which may include also
1268+
// branches that are available in friends. Only set the branch status
1269+
// if it has a value provided by the user
1270+
Int_t status = chainEl->GetStatus();
1271+
if (status != -1)
1272+
fTree->SetBranchStatus(chainEl->GetName(), status);
1273+
1274+
// Set the branch addresses for the newly opened file.
1275+
void *addr = chainEl->GetBaddress();
1276+
if (!addr)
1277+
continue;
1278+
1279+
TBranch *br = fTree->GetBranch(chainEl->GetName());
1280+
TBranch **pp = chainEl->GetBranchPtr();
1281+
if (pp) {
1282+
// FIXME: What if br is zero here?
1283+
*pp = br;
1284+
}
1285+
if (!br)
1286+
continue;
1287+
1288+
if (!chainEl->GetCheckedType()) {
1289+
Int_t res = CheckBranchAddressType(br, TClass::GetClass(chainEl->GetBaddressClassName()),
1290+
(EDataType)chainEl->GetBaddressType(), chainEl->GetBaddressIsPtr());
1291+
if ((res & kNeedEnableDecomposedObj) && !br->GetMakeClass()) {
1292+
br->SetMakeClass(true);
1293+
}
1294+
chainEl->SetDecomposedObj(br->GetMakeClass());
1295+
chainEl->SetCheckedType(true);
1296+
}
1297+
// FIXME: We may have to tell the branch it should
1298+
// not be an owner of the object pointed at.
1299+
br->SetAddress(addr);
1300+
if (TestBit(kAutoDelete)) {
1301+
br->SetAutoDelete(true);
1302+
}
1303+
}
1304+
if (fPlayer) {
1305+
fPlayer->UpdateFormulaLeaves();
1306+
}
1307+
// Notify user if requested.
1308+
if (fNotify) {
1309+
if (!fNotify->Notify())
1310+
return -6;
1311+
}
1312+
1313+
return 0;
1314+
}
1315+
12341316
////////////////////////////////////////////////////////////////////////////////
12351317
/// Find the tree which contains entry, and set it as the current tree.
12361318
///
@@ -1294,93 +1376,34 @@ Long64_t TChain::LoadTree(Long64_t entry)
12941376

12951377
// If entry belongs to the current tree return entry.
12961378
if (fTree && treenum == fTreeNumber) {
1297-
// First set the entry the tree on its owns friends
1298-
// (the friends of the chain will be updated in the
1299-
// next loop).
1379+
// First load entry on the current tree, this will set the cursor also
1380+
// on its friend trees. Their branch addresses cannot be updated yet,
1381+
// as the required branch names are only available via `fStatus`, i.e.
1382+
// only the chain knows about them. This is taken care of in the following
1383+
// RefreshFriendAddresses call
13001384
fTree->LoadTree(treeReadEntry);
13011385

1302-
if (fFriends) {
1303-
// The current tree has not changed but some of its friends might.
1304-
//
1305-
TIter next(fFriends);
1386+
if (fFriends || fTree->GetListOfFriends()) {
13061387
TFriendLock lock(this, kLoadTree);
1307-
TFriendElement* fe = nullptr;
1308-
while ((fe = (TFriendElement*) next())) {
1309-
TTree* at = fe->GetTree();
1310-
// If the tree is a
1311-
// direct friend of the chain, it should be scanned
1312-
// used the chain entry number and NOT the tree entry
1313-
// number (treeReadEntry) hence we do:
1314-
at->LoadTreeFriend(entry, this);
1315-
}
1316-
bool needUpdate = false;
1317-
if (fTree->GetListOfFriends()) {
1318-
for(auto fetree : ROOT::Detail::TRangeStaticCast<TFriendElement>(*fTree->GetListOfFriends())) {
1319-
if (fetree->IsUpdated()) {
1320-
needUpdate = true;
1321-
fetree->ResetUpdated();
1322-
}
1388+
if (fFriends) {
1389+
// Make sure we load friends of the chain aligned to the current global entry number
1390+
for (auto *frEl : ROOT::Detail::TRangeStaticCast<TFriendElement>(*fFriends)) {
1391+
auto *frTree = frEl->GetTree();
1392+
frTree->LoadTreeFriend(entry, this);
13231393
}
13241394
}
1325-
if (needUpdate) {
1326-
// Update the branch/leaf addresses and
1327-
// the list of leaves in all TTreeFormula of the TTreePlayer (if any).
1328-
1329-
// Set the branch statuses for the newly opened file.
1330-
TChainElement *frelement;
1331-
TIter fnext(fStatus);
1332-
while ((frelement = (TChainElement*) fnext())) {
1333-
Int_t status = frelement->GetStatus();
1334-
// Only set the branch status if it has a value provided
1335-
// by the user
1336-
if (status != -1)
1337-
fTree->SetBranchStatus(frelement->GetName(), status);
1338-
}
13391395

1340-
// Set the branch addresses for the newly opened file.
1341-
fnext.Reset();
1342-
while ((frelement = (TChainElement*) fnext())) {
1343-
void* addr = frelement->GetBaddress();
1344-
if (addr) {
1345-
TBranch* br = fTree->GetBranch(frelement->GetName());
1346-
TBranch** pp = frelement->GetBranchPtr();
1347-
if (pp) {
1348-
// FIXME: What if br is zero here?
1349-
*pp = br;
1350-
}
1351-
if (br) {
1352-
if (!frelement->GetCheckedType()) {
1353-
Int_t res = CheckBranchAddressType(br, TClass::GetClass(frelement->GetBaddressClassName()),
1354-
(EDataType) frelement->GetBaddressType(), frelement->GetBaddressIsPtr());
1355-
if ((res & kNeedEnableDecomposedObj) && !br->GetMakeClass()) {
1356-
br->SetMakeClass(true);
1357-
}
1358-
frelement->SetDecomposedObj(br->GetMakeClass());
1359-
frelement->SetCheckedType(true);
1360-
}
1361-
// FIXME: We may have to tell the branch it should
1362-
// not be an owner of the object pointed at.
1363-
br->SetAddress(addr);
1364-
if (TestBit(kAutoDelete)) {
1365-
br->SetAutoDelete(true);
1366-
}
1367-
}
1368-
}
1369-
}
1370-
if (fPlayer) {
1371-
fPlayer->UpdateFormulaLeaves();
1372-
}
1373-
// Notify user if requested.
1374-
if (fNotify) {
1375-
if(!fNotify->Notify()) return -6;
1376-
}
1377-
}
1396+
// Now refresh branch addresses of friend trees. This acts on the current tree of the chain, whether the
1397+
// friends are friends of the chain or friends of the tree itself.
1398+
if (auto refreshFriendAddressesRet = RefreshFriendAddresses(); refreshFriendAddressesRet == -6)
1399+
return refreshFriendAddressesRet;
13781400
}
1401+
13791402
return treeReadEntry;
13801403
}
13811404

13821405
if (fExternalFriends) {
1383-
for(auto external_fe : ROOT::Detail::TRangeStaticCast<TFriendElement>(*fExternalFriends)) {
1406+
for (auto external_fe : ROOT::Detail::TRangeStaticCast<TFriendElement>(*fExternalFriends)) {
13841407
external_fe->MarkUpdated();
13851408
}
13861409
}

tree/treeplayer/test/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ endif()
1717

1818
ROOT_ADD_GTEST(treeplayer_gh16804 gh16804.cxx LIBRARIES TreePlayer)
1919

20+
ROOT_ADD_GTEST(treeplayer_gh16805 gh16805.cxx LIBRARIES TreePlayer)
21+
2022
ROOT_ADD_GTEST(treeplayer_leafs leafs.cxx LIBRARIES TreePlayer)
2123
add_custom_command(TARGET treeplayer_leafs POST_BUILD
2224
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/data.h data.h)
@@ -39,3 +41,5 @@ if(imt)
3941
ROOT_ADD_GTEST(treeprocessormt_remotefiles treeprocs/treeprocessormt_remotefiles.cxx LIBRARIES TreePlayer)
4042
endif()
4143
endif()
44+
45+

0 commit comments

Comments
 (0)