PyROOT memory management issue for copied TTree with friends


ROOT Version: 6.12/06
Platform: SLC6 (x86_64-slc6-gcc62-opt using LCG 93 from cvmfs)
Compiler: gcc62
Python: 2.7.13


Also tested:


ROOT Version: 6.14/06
Platform: OSX 10.14.5 (installed via Homebrew)
Compiler: clang
Python: 3.7.2


Dear experts,

If I copy a TTree which has friends in PyROOT, there seems to be some issue with who owns the copied TTree’s friends list if I access the friends list in python.

Minimal reproducer:

import ROOT

f1 = ROOT.TFile('file1.root', 'recreate')
tree1 = ROOT.TTree('tree1', 'tree1')

tree2 = ROOT.TTree('tree2', 'tree2')
tree2.Write()

tree1.AddFriend(tree2)
tree1.Write()
f1.Close()

f1 = ROOT.TFile('file1.root')
tree1 = f1.Get('tree1')

f2 = ROOT.TFile('file2.root', 'recreate')
tree3 = tree1.CopyTree('')
# This causes a crash when f2 is closed.
tree3.GetListOfFriends()

print ('f2.Close()')
f2.Close()
print ('f1.Close()')
f1.Close()

My current workaround is to switch

tree3.GetListOfFriends()

with

ROOT.gROOT.ProcessLine(tree3.GetName() + '->GetListOfFriends()')

I actually want to clear the list of friends in the copied TTree, but just accessing it in python is enough to cause a crash. I’ve also found that if I close f1 before f2 there’s no crash.

Any thoughts?

As said, I have a workaround, so it’s not a showstopper, just peculiar.

Cheers,
Michael

@etejedor @pcanal can we hear your thoughts on this?

Hi,

So this is the stack trace as a result of running f2.Close():

#0  0x00007ffff6d03207 in raise () from /lib64/libc.so.6
#1  0x00007ffff6d048f8 in abort () from /lib64/libc.so.6
#2  0x00007fffed7c1ec5 in __gnu_cxx::__verbose_terminate_handler () at /afs/cern.ch/cms/CAF/CMSCOMM/COMM_ECAL/dkonst/GCC/build/contrib/gcc-7.3.0/src/gcc/7.3.0/libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00007fffed7bfc96 in __cxxabiv1::__terminate (handler=<optimized out>) at /afs/cern.ch/cms/CAF/CMSCOMM/COMM_ECAL/dkonst/GCC/build/contrib/gcc-7.3.0/src/gcc/7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:47
#4  0x00007fffed7bfce1 in std::terminate () at /afs/cern.ch/cms/CAF/CMSCOMM/COMM_ECAL/dkonst/GCC/build/contrib/gcc-7.3.0/src/gcc/7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:57
#5  0x00007fffed7c0a3f in __cxxabiv1::__cxa_pure_virtual () at /afs/cern.ch/cms/CAF/CMSCOMM/COMM_ECAL/dkonst/GCC/build/contrib/gcc-7.3.0/src/gcc/7.3.0/libstdc++-v3/libsupc++/pure.cc:50
#6  0x00007fffede9066a in TCollection::RecursiveRemove (this=0x299b330, obj=0x299b330) at /home/etejedor/root/fork/root/core/cont/src/TCollection.cxx:577
#7  0x00007fffef511570 in TTree::RecursiveRemove (this=0x299aee0, obj=0x299b330) at /home/etejedor/root/fork/root/tree/tree/src/TTree.cxx:7628
#8  0x00007fffede9a5fd in TList::RecursiveRemove (this=0x299b5b0, obj=0x299b330) at /home/etejedor/root/fork/root/core/cont/src/TList.cxx:810
#9  0x00007fffede945d4 in THashList::RecursiveRemove (this=0x73b260, obj=0x299b330) at /home/etejedor/root/fork/root/core/cont/src/THashList.cxx:354
#10 0x00007fffeddce847 in TROOT::RecursiveRemove (this=0x7fffee2cf180 <ROOT::Internal::GetROOT1()::alloc>, obj=0x299b330) at /home/etejedor/root/fork/root/core/base/src/TROOT.cxx:2498
#11 0x00007fffefcb160f in ROOT::CallRecursiveRemoveIfNeeded (obj=...) at /home/etejedor/root/fork/build_oldpyroot_py2/include/TROOT.h:408
#12 0x00007fffede8f324 in TCollection::~TCollection (this=0x299b330, __in_chrg=<optimized out>) at /home/etejedor/root/fork/root/core/cont/src/TCollection.cxx:187
#13 0x00007fffede849ea in TSeqCollection::~TSeqCollection (this=0x299b330, __in_chrg=<optimized out>) at /home/etejedor/root/fork/build_oldpyroot_py2/include/TSeqCollection.h:37
#14 0x00007fffede977d5 in TList::~TList (this=0x299b330, __in_chrg=<optimized out>) at /home/etejedor/root/fork/root/core/cont/src/TList.cxx:89
#15 0x00007fffede977f0 in TList::~TList (this=0x299b330, __in_chrg=<optimized out>) at /home/etejedor/root/fork/root/core/cont/src/TList.cxx:92
#16 0x00007fffef4fd211 in TTree::~TTree (this=0x299aee0, __in_chrg=<optimized out>) at /home/etejedor/root/fork/root/tree/tree/src/TTree.cxx:951
#17 0x00007fffef4fd6bc in TTree::~TTree (this=0x299aee0, __in_chrg=<optimized out>) at /home/etejedor/root/fork/root/tree/tree/src/TTree.cxx:999
#18 0x00007fffede90fcb in TCollection::GarbageCollect (obj=0x299aee0) at /home/etejedor/root/fork/root/core/cont/src/TCollection.cxx:734
#19 0x00007fffede9960b in TList::Delete (this=0x28c1170, option=0x7fffee9b300c "") at /home/etejedor/root/fork/root/core/cont/src/TList.cxx:534
#20 0x00007fffede93c29 in THashList::Delete (this=0x28c1170, option=0x7fffee9b300c "") at /home/etejedor/root/fork/root/core/cont/src/THashList.cxx:215
#21 0x00007fffee7794f9 in TDirectoryFile::Close (this=0x2418610, option=0x7ffff7fd6000 "") at /home/etejedor/root/fork/root/io/io/src/TDirectoryFile.cxx:584
#22 0x00007fffee7911e7 in TFile::Close (this=0x2418610, option=0x7ffff7fd6000 "") at /home/etejedor/root/fork/root/io/io/src/TFile.cxx:951
#23 0x00007ffff7fd703d in ?? ()
#24 0x0000000000000000 in ?? ()

When f2 is closed, we end up calling TCollection::RecursiveRemove on the list of friends of tree3 (which is the same as the one of tree1, i.e. [tree2]). But the object we are recursively removing is the list of friends itself:

#6 0x00007fffede9066a in TCollection::RecursiveRemove (this=0x299b330, obj=0x299b330) at

So it seems that the fact of getting the list of friends of tree3 (and thus creating a Python proxy for it) makes the list of friends be added to the internal list of cleanups of ROOT.

If you explicitly request ROOT not to clean the list:

l = tree3.GetListOfFriends()
l.SetBit(ROOT.kMustCleanup, False)

you do not see the crash anymore.

Might or might not be related: https://sft.its.cern.ch/jira/browse/ROOT-9283

Thanks! This is a cleaner workaround than using gROOT.ProcessLine.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.