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