Segmenation violation when using static TFile

Dear experts,

I have a singleton class with a TFile * member, implemented via a static instance.
When the program ends, the singleton destructor, which takes care of writing and closing the TFile, is called, and the program crashes with segmentation violation.

I managed to reproduce this issue with a minimal example, and the problem is due to the fact that gROOT is destroyed before the static singleton instance.
TROOT::~TROOT closes the TFile and so when I try to access it later, it crashes.
I can avoid the crash by checking that TFile::IsOpen is true before calling TFile::Write in the singleton destructor. However, the problem is also that in the TROOT destructor, TFile::Write is not called, and so the TFile on disk results empty and/or corrupted.
I didn’t find any way of avoiding gROOT manage my TFile: I tried with TFile::AddDirectory(false) before doing new TFile, but the TFile is still added to the list of files in gROOT.
I tried with gROOT->GetListOfFiles()->Clear("nodelete"); before the program exits: now gROOT does not close the TFile, but the segmentation violation still occurs. Note that in this case, checking TFile::IsOpen does not prevent a crash, because it returns true, but TFile::Write still fails.

Here’s the minimal example code, which tests creating a TFile using or not using a static variable: test.C (2.0 KB)
I compile it with:

$(root-config --cxx) -g -fno-omit-frame-pointer -Wall -Wl,--no-as-needed $(root-config --cflags) -L$(root-config --libdir) -lCore -lRIO -lTree test.C -o test.exe

This is the output without using static:

 ??? ENTERING IN Test* create(bool)
 ??? ALLOCATING NEW INSTANCE
 ??? ENTERING IN Test::Test()
 ??? mFile = 0x55cd0649c400
 ??? EXITING FROM Test::Test()
 ??? EXITING FROM Test* create(bool)
TFile: name=test.root, title=, option=CREATE
******************************************************************************
*Tree    :info      : info                                                   *
*Entries :        1 : Total =             932 bytes  File  Size =          0 *
*        :          : Tree compression factor =   1.00                       *
******************************************************************************
*Br    0 :var       : var/I                                                  *
*Entries :        1 : Total  Size=        637 bytes  One basket in memory    *
*Baskets :        0 : Basket Size=      32000 bytes  Compression=   1.00     *
*............................................................................*
************************
*    Row   *   var.var *
************************
*        0 *         1 *
************************
 ??? MANUALLY DELETING INSTANCE
 ??? ENTERING IN Test::~Test()
 ??? mFile = 0x55cd0649c400
 ??? mFile->IsOpen() = 1
 ??? EXITING FROM Test::~Test()
Collection name='Files', class='TList', size=0
 ??? EXITING PROGRAM

This is the output using static:

 ??? ENTERING IN Test* create(bool)
 ??? USING STATIC INSTANCE
 ??? ENTERING IN Test::Test()
 ??? mFile = 0x55cc04e0d3c0
 ??? EXITING FROM Test::Test()
 ??? EXITING FROM Test* create(bool)
TFile: name=test.root, title=, option=CREATE
******************************************************************************
*Tree    :info      : info                                                   *
*Entries :        1 : Total =             932 bytes  File  Size =          0 *
*        :          : Tree compression factor =   1.00                       *
******************************************************************************
*Br    0 :var       : var/I                                                  *
*Entries :        1 : Total  Size=        637 bytes  One basket in memory    *
*Baskets :        0 : Basket Size=      32000 bytes  Compression=   1.00     *
*............................................................................*
************************
*    Row   *   var.var *
************************
*        0 *         1 *
************************
Collection name='Files', class='TList', size=1
 TFile: name=test.root, title=, option=CREATE
******************************************************************************
*Tree    :info      : info                                                   *
*Entries :        1 : Total =             932 bytes  File  Size =          0 *
*        :          : Tree compression factor =   1.00                       *
******************************************************************************
*Br    0 :var       : var/I                                                  *
*Entries :        1 : Total  Size=        637 bytes  One basket in memory    *
*Baskets :        0 : Basket Size=      32000 bytes  Compression=   1.00     *
*............................................................................*
 ??? EXITING PROGRAM
 ??? ENTERING IN Test::~Test()
 ??? mFile = 0x55cc04e0d3c0
 ??? mFile->IsOpen() = 0
Warning in <TFile::Write>: file test.root not opened in write mode
 ??? EXITING FROM Test::~Test()
Segmentation fault

Here’s the backtrace from gdb:

#0  0x00007ffff70907c3 in unlink_chunk (p=0x5555574ce200, av=0x7ffff7208c80 <main_arena>) at ./malloc/malloc.c:1634
#1  0x00007ffff70909e9 in malloc_consolidate (av=av@entry=0x7ffff7208c80 <main_arena>) at ./malloc/malloc.c:4780
#2  0x00007ffff7091f20 in _int_free (av=0x7ffff7208c80 <main_arena>, p=0x555555a8d6d0, have_lock=<optimized out>) at ./malloc/malloc.c:4674
#3  0x00007ffff70944d3 in __GI___libc_free (mem=<optimized out>) at ./malloc/malloc.c:3391
#4  0x00007ffff4e15ef7 in clang::ModuleMap::~ModuleMap() () from /programs/root6/install/lib/libCling.so
#5  0x00007ffff4e6b1b0 in clang::Preprocessor::~Preprocessor() () from /programs/root6/install/lib/libCling.so
#6  0x00007ffff21c3c8a in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x5555556447f0) at /usr/include/c++/11/bits/shared_ptr_base.h:168
#7  0x00007ffff27b89ed in clang::CompilerInstance::~CompilerInstance() () from /programs/root6/install/lib/libCling.so
#8  0x00007ffff27b8d3d in clang::CompilerInstance::~CompilerInstance() () from /programs/root6/install/lib/libCling.so
#9  0x00007ffff2216fbd in cling::Interpreter::~Interpreter() () from /programs/root6/install/lib/libCling.so
#10 0x00007ffff22174ed in cling::Interpreter::~Interpreter() () from /programs/root6/install/lib/libCling.so
#11 0x00007ffff2143667 in std::default_delete<cling::Interpreter>::operator() (__ptr=<optimized out>, this=<optimized out>) at /usr/include/c++/11/bits/unique_ptr.h:79
#12 std::unique_ptr<cling::Interpreter, std::default_delete<cling::Interpreter> >::~unique_ptr (this=0x5555555f4448, __in_chrg=<optimized out>) at /usr/include/c++/11/bits/unique_ptr.h:361
#13 TCling::~TCling (this=0x5555555f4210, __in_chrg=<optimized out>) at /programs/root6/git/core/metacling/src/TCling.cxx:1649
#14 0x00007ffff21438bd in TCling::~TCling (this=0x5555555f4210, __in_chrg=<optimized out>) at /programs/root6/git/core/metacling/src/TCling.cxx:1649
#15 0x00007ffff7c9028f in TROOT::~TROOT (this=0x7ffff7f872c0 <ROOT::Internal::GetROOT1()::alloc>, __in_chrg=<optimized out>) at /programs/root6/git/core/base/src/TROOT.cxx:992
#16 0x00007ffff7034495 in __run_exit_handlers (status=0, listp=0x7ffff7208838 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:113
#17 0x00007ffff7034610 in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:143
#18 0x00007ffff7018d97 in __libc_start_call_main (main=main@entry=0x5555555556b4 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7fffffffd7c8) at ../sysdeps/nptl/libc_start_call_main.h:74
#19 0x00007ffff7018e40 in __libc_start_main_impl (main=0x5555555556b4 <main(int, char**)>, argc=2, argv=0x7fffffffd7c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffd7b8) at ../csu/libc-start.c:392
#20 0x0000555555555365 in _start ()

while this is the backtrace in case of gROOT->GetListOfFiles()->Clear("nodelete"):

#0  0x00007ffff7092b41 in _int_malloc (av=av@entry=0x7ffff7208c80 <main_arena>, bytes=bytes@entry=19) at ./malloc/malloc.c:3937
#1  0x00007ffff70942e2 in __GI___libc_malloc (bytes=19) at ./malloc/malloc.c:3321
#2  0x00007ffff73ce9cc in operator new(unsigned long) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7da3a51 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*> (__end=0x5555574852e2 "", __beg=0x5555574852d0 "TStreamerBasicType", 
    this=0x555557913330) at /usr/include/c++/11/bits/basic_string.tcc:219
#4  std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> > (__a=..., __s=0x5555574852d0 "TStreamerBasicType", this=0x555557913330)
    at /usr/include/c++/11/bits/basic_string.h:539
#5  __gnu_cxx::new_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::construct<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char const*&> (__p=0x555557913330, this=0x7fffffffca10) at /usr/include/c++/11/ext/new_allocator.h:162
#6  std::allocator_traits<std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::construct<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char const*&> (__p=0x555557913330, __a=...) at /usr/include/c++/11/bits/alloc_traits.h:516
#7  std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_M_realloc_insert<char const*&> (this=0x7fffffffca10, __position=non-dereferenceable iterator for std::vector) at /usr/include/c++/11/bits/vector.tcc:449
#8  0x00007ffff215383f in std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::emplace_back<char const*&> (this=0x7fffffffca10) at /usr/include/c++/11/bits/vector.tcc:121
#9  TCling::AutoParseImplRecurse (this=0x5555555f4210, cls=<optimized out>, topLevel=<optimized out>) at /programs/root6/git/core/metacling/src/TCling.cxx:6335
#10 0x00007ffff215a64d in TCling::AutoParse (this=0x5555555f4210, cls=0x5555574852d0 "TStreamerBasicType") at /programs/root6/git/core/metacling/src/TCling.cxx:6459
#11 0x00007ffff7d86369 in TClass::LoadClassInfo (this=0x555557163530) at /programs/root6/git/core/meta/src/TClass.cxx:5822
#12 0x00007ffff7d878c5 in TClass::GetClassInfo (this=0x555557163530) at /programs/root6/git/core/meta/inc/TClass.h:432
#13 TClass::GetBaseClassOffset (isDerivedObject=true, address=0x0, toBase=0x555556127690, this=0x555557163530) at /programs/root6/git/core/meta/src/TClass.cxx:2807
#14 TClass::GetBaseClassOffset (this=this@entry=0x555557163530, toBase=toBase@entry=0x555556127690, address=address@entry=0x0, isDerivedObject=isDerivedObject@entry=true)
    at /programs/root6/git/core/meta/src/TClass.cxx:2789
#15 0x00007ffff77f70f2 in TBufferIO::WriteObjectAny (this=0x55555775b1f0, obj=0x555557481eb0, ptrClass=0x555556127690, cacheReuse=<optimized out>)
    at /programs/root6/git/io/io/src/TBufferIO.cxx:518
#16 0x00007ffff7d562f5 in operator<< <TObject> (obj=0x555557481eb0, buf=...) at /programs/root6/git/core/base/inc/TBuffer.h:402
#17 TObjArray::Streamer (this=0x7fffffffd120, b=...) at /programs/root6/git/core/cont/src/TObjArray.cxx:487
#18 0x00007ffff77efb72 in TClass::Streamer (onfile_class=0x0, b=..., obj=0x7fffffffd120, this=0x555556ff7990) at /programs/root6/git/core/meta/inc/TClass.h:609
#19 TBufferFile::WriteObjectClass (this=0x55555775b1f0, actualObjectStart=0x7fffffffd120, actualClass=0x555556ff7990, cacheReuse=<optimized out>)
    at /programs/root6/git/io/io/src/TBufferFile.cxx:2553
#20 0x00007ffff77f6fd7 in TBufferIO::WriteObjectAny (this=this@entry=0x55555775b1f0, obj=obj@entry=0x7fffffffd120, ptrClass=0x555556ff7990, cacheReuse=cacheReuse@entry=false)
    at /programs/root6/git/io/io/src/TBufferIO.cxx:522
#21 0x00007ffff78a9a8f in TStreamerInfo::Streamer (this=<optimized out>, R__b=...) at /programs/root6/git/io/io/src/TStreamerInfo.cxx:5360
#22 0x00007ffff77efb72 in TClass::Streamer (onfile_class=0x0, b=..., obj=0x555556fc6ad0, this=0x5555561e1a40) at /programs/root6/git/core/meta/inc/TClass.h:609
#23 TBufferFile::WriteObjectClass (this=0x55555775b1f0, actualObjectStart=0x555556fc6ad0, actualClass=0x5555561e1a40, cacheReuse=<optimized out>)
    at /programs/root6/git/io/io/src/TBufferFile.cxx:2553
#24 0x00007ffff77f710c in TBufferIO::WriteObjectAny (this=0x55555775b1f0, obj=0x555556fc6ad0, ptrClass=0x555556127690, cacheReuse=<optimized out>)
    at /programs/root6/git/io/io/src/TBufferIO.cxx:519
#25 0x00007ffff7d50e84 in operator<< <TObject> (obj=0x555556fc6ad0, buf=...) at /programs/root6/git/core/base/inc/TBuffer.h:402
#26 TList::Streamer (this=<optimized out>, b=...) at /programs/root6/git/core/cont/src/TList.cxx:1255
#27 0x00007ffff7885b6d in TKey::TKey (this=this@entry=0x7fffffffd4e0, obj=obj@entry=0x7fffffffd400, name=name@entry=0x7ffff7a6ffb4 "StreamerInfo", bufsize=1559, motherDir=motherDir@entry=0x5555555e23c0)
    at /programs/root6/git/io/io/src/TKey.cxx:249
#28 0x00007ffff7866310 in TFile::WriteStreamerInfo (this=0x5555555e23c0) at /programs/root6/git/io/io/src/TFile.cxx:3796
#29 0x00007ffff7865a71 in TFile::Write (this=0x5555555e23c0, opt=0, bufsiz=<optimized out>) at /programs/root6/git/io/io/src/TFile.cxx:2398
#30 0x0000555555555d62 in Test::~Test (this=0x555555558290 <create(bool)::ts>, __in_chrg=<optimized out>) at test.C:47
#31 0x00007ffff7034495 in __run_exit_handlers (status=0, listp=0x7ffff7208838 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:113
#32 0x00007ffff7034610 in __GI_exit (status=<optimized out>) at ./stdlib/exit.c:143
#33 0x00007ffff7018d97 in __libc_start_call_main (main=main@entry=0x5555555556b4 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7fffffffd7c8) at ../sysdeps/nptl/libc_start_call_main.h:74
#34 0x00007ffff7018e40 in __libc_start_main_impl (main=0x5555555556b4 <main(int, char**)>, argc=2, argv=0x7fffffffd7c8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffd7b8) at ../csu/libc-start.c:392
#35 0x0000555555555365 in _start ()

Best,
Claudio


ROOT Version: 6.26/02
Platform: Ubuntu 22.04
Compiler: gcc


I guess @pcanal can help you.

You can not safely delete/Close a TFile after gROOT is destroyed. The closure and/or final writing of the TFile requires services from TROOT and its TClass. (i.e. even if you could make it ‘go’ without crashing, the data would likely be wrong/incorrect/incomplete).

So, unless you can set your environment to guarantee that you static is destructed before TROOT, it will not work. In addition, if you TFile or TTree depends on the content of shared libraries (for example you stored some objects in the TTree) their writing must also happens before those libraries are unloaded (otherwise you might lose data).

The best recommendation is to make sure that all TFile are finalized and closed before the end of main.

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