diff --git a/core/base/inc/TROOT.h b/core/base/inc/TROOT.h index 6ddff151842e8..a4c03af567bc2 100644 --- a/core/base/inc/TROOT.h +++ b/core/base/inc/TROOT.h @@ -344,6 +344,7 @@ friend TROOT *ROOT::Internal::GetROOT2(); Int_t Timer() const { return fTimer; } //---- static functions + static void CleanUpROOTAtExit(); static Int_t DecreaseDirLevel(); static Int_t GetDirLevel(); static const char *GetMacroPath(); @@ -356,6 +357,7 @@ friend TROOT *ROOT::Internal::GetROOT2(); static Int_t ConvertVersionCode2Int(Int_t code); static Int_t ConvertVersionInt2Code(Int_t v); static Int_t RootVersionCode(); + static void WriteCloseAllFiles(); static const std::vector &AddExtraInterpreterArgs(const std::vector &args); static const char**&GetExtraInterpreterArgs(); diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 64222d36bdd22..832edf9162d48 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -119,6 +119,7 @@ FARPROC dlsym(void *library, const char *function_name) #include "TClass.h" #include "TClassEdit.h" #include "TClassGenerator.h" +#include "TDirectory.h" #include "TDataType.h" #include "TStyle.h" #include "TObjectTable.h" @@ -226,10 +227,33 @@ static Int_t ITIMQQ(const char *time) return 100*hh + mm; } +//////////////////////////////////////////////////////////////////////////////// +/// Write and Close all open writable TFiles, useful to be called when SIGTERM is caught. + +void TROOT::WriteCloseAllFiles() +{ + if (gROOT) { + R__LOCKGUARD(gROOTMutex); + + if (gROOT->GetListOfFiles()) { + TIter next(gROOT->GetListOfFiles()); + while (TObject *obj = next()) { + if (obj && obj->InheritsFrom(TClass::GetClass("TFile", kFALSE, kTRUE))) { + auto fobj = static_cast(obj); + if (fobj->IsWritable()) { + fobj->Write(); + fobj->Close(); + } + } + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// /// Clean up at program termination before global objects go out of scope. -static void CleanUpROOTAtExit() +void TROOT::CleanUpROOTAtExit() { if (gROOT) { R__LOCKGUARD(gROOTMutex); diff --git a/core/textinput/src/textinput/TerminalConfigUnix.cpp b/core/textinput/src/textinput/TerminalConfigUnix.cpp index e23106139d7e7..ae181845f1d8c 100644 --- a/core/textinput/src/textinput/TerminalConfigUnix.cpp +++ b/core/textinput/src/textinput/TerminalConfigUnix.cpp @@ -24,6 +24,8 @@ #include #include +#include + using namespace textinput; using std::memcpy; using std::signal; @@ -104,6 +106,11 @@ TerminalConfigUnix::HandleSignal(int signum) { } } + // gentle save and close if SIGTERM + if (signum == SIGTERM) { + TROOT::WriteCloseAllFiles(); + TROOT::CleanUpROOTAtExit(); + } // No previous handler found, re-raise to get default handling: signal(signum, SIG_DFL); // unregister ourselves raise(signum); // terminate through default handler diff --git a/io/io/test/CMakeLists.txt b/io/io/test/CMakeLists.txt index e61201d5c9b25..2844137597444 100644 --- a/io/io/test/CMakeLists.txt +++ b/io/io/test/CMakeLists.txt @@ -5,7 +5,7 @@ # For the list of contributors see $ROOTSYS/README/CREDITS. ROOT_ADD_GTEST(RRawFile RRawFile.cxx LIBRARIES RIO) -ROOT_ADD_GTEST(TFile TFileTests.cxx LIBRARIES RIO) +ROOT_ADD_GTEST(TFile TFileTests.cxx LIBRARIES RIO Hist) ROOT_ADD_GTEST(TBufferFile TBufferFileTests.cxx LIBRARIES RIO) ROOT_ADD_GTEST(TBufferMerger TBufferMerger.cxx LIBRARIES RIO Imt Tree) ROOT_ADD_GTEST(TBufferJSON TBufferJSONTests.cxx LIBRARIES RIO) diff --git a/io/io/test/TFileTests.cxx b/io/io/test/TFileTests.cxx index 94f4e42e38196..a9aba82463fe3 100644 --- a/io/io/test/TFileTests.cxx +++ b/io/io/test/TFileTests.cxx @@ -5,6 +5,7 @@ #include "gtest/gtest.h" #include "TFile.h" +#include "TH1I.h" #include "TMemFile.h" #include "TDirectory.h" #include "TKey.h" @@ -251,3 +252,25 @@ TEST(TFile, WalkTKeys) EXPECT_EQ(it->fKeyName, kLongerKey); EXPECT_EQ(it->fClassName, "string"); } + +// https://github.com/root-project/root/issues/13300 +TEST(TFile, Sigterm) +{ + auto filename = "out13300.root"; + { + TFile file(filename, "RECREATE"); + file.mkdir("subdir")->cd(); + TH1I hist("h", "h", 10, 0., 1.); + hist.Fill(0.4); + // Since the real behavior is save+close+crash which would make the test fail, + // rather than calling directly std::raise(SIGTERM), + // we emulate the response to SIGTERM in TerminalConfigUnix before crash: + TROOT::WriteCloseAllFiles(); + TROOT::CleanUpROOTAtExit(); + } + { + TFile file(filename, "READ"); + ASSERT_EQ(file.Get("subdir/h")->GetBinContent(5), 1); + } + gSystem->Unlink(filename); +}