diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index fda8d764cae10..fc5a9df5eefc2 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -157,10 +157,11 @@ jobs: INCREMENTAL: ${{ !contains(github.event.pull_request.labels.*.name, 'clean build') && !matrix.platform == 'mac15' && !matrix.platform == 'mac26'}} GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }} OVERRIDES: ${{ join( matrix.overrides, ' ') }} + ROOT_IMPLICIT_OWNERSHIP: 0 run: | [ -d "${VIRTUAL_ENV_DIR}" ] && source ${VIRTUAL_ENV_DIR}/bin/activate echo "Python is now $(which python3) $(python3 --version)" - src/.github/workflows/root-ci-config/build_root.py \ + src/.github/workflows/root-ci-config/build_root.py \ --buildtype RelWithDebInfo \ --incremental $INCREMENTAL \ --base_ref ${{ github.base_ref }} \ @@ -512,6 +513,7 @@ jobs: INCREMENTAL: ${{ !contains(github.event.pull_request.labels.*.name, 'clean build') }} GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }} OVERRIDES: ${{ join( matrix.overrides, ' ') }} + ROOT_IMPLICIT_OWNERSHIP: 0 run: ".github/workflows/root-ci-config/build_root.py --buildtype RelWithDebInfo --platform ${{ matrix.image }} diff --git a/bindings/pyroot/pythonizations/test/tfile_context_manager.py b/bindings/pyroot/pythonizations/test/tfile_context_manager.py index 7ab1f1e1b17e8..257ab262a5c6c 100644 --- a/bindings/pyroot/pythonizations/test/tfile_context_manager.py +++ b/bindings/pyroot/pythonizations/test/tfile_context_manager.py @@ -64,6 +64,7 @@ def test_filewrite(self): histoname = "myhisto_3" with TFile(filename, "recreate") as outfile: hout = ROOT.TH1F(histoname, histoname, self.NBINS, self.XMIN, self.XMAX) + hout.SetDirectory(outfile) outfile.Write() self.check_file_data(outfile, filename, histoname) diff --git a/cmake/modules/RootTestDriver.cmake b/cmake/modules/RootTestDriver.cmake index 4579acca8b1e5..6ee47d9012e09 100644 --- a/cmake/modules/RootTestDriver.cmake +++ b/cmake/modules/RootTestDriver.cmake @@ -195,6 +195,7 @@ if(CMD) string(STRIP "${_errvar0}" _errvar0) string(REPLACE "\n" ";" _lines "${_errvar0}") list(FILTER _lines EXCLUDE REGEX "^Info in <.+::ACLiC>: creating shared library.+") + list(FILTER _lines EXCLUDE REGEX "^Info in : Implicit object ownership.*") # ROOT 7 demo mode in ROOT 6 string(REPLACE ";" "\n" _errvar0 "${_lines}") if(_errvar0) message(FATAL_ERROR "Unexpected error output") diff --git a/core/base/inc/TROOT.h b/core/base/inc/TROOT.h index f7692a5db7da2..a049cf3d69356 100644 --- a/core/base/inc/TROOT.h +++ b/core/base/inc/TROOT.h @@ -97,6 +97,11 @@ namespace ROOT { void DisableImplicitMT(); Bool_t IsImplicitMTEnabled(); UInt_t GetThreadPoolSize(); + namespace Experimental { + void EnableImplicitObjectOwnership(); + void DisableImplicitObjectOwnership(); + bool IsImplicitObjectOwnershipEnabled(); + } // namespace Experimental } class TROOT : public TDirectory { diff --git a/core/base/src/TDirectory.cxx b/core/base/src/TDirectory.cxx index a8bcee69e8db1..3afb50329aa47 100644 --- a/core/base/src/TDirectory.cxx +++ b/core/base/src/TDirectory.cxx @@ -207,8 +207,10 @@ void TDirectory::Append(TObject *obj, Bool_t replace /* = kFALSE */) if (replace && obj->GetName() && obj->GetName()[0]) { TObject *old; while (nullptr != (old = GetList()->FindObject(obj->GetName()))) { - Warning("Append","Replacing existing %s: %s (Potential memory leak).", - obj->IsA()->GetName(),obj->GetName()); + if (obj != old) { + Warning("Append","Replacing existing %s: %s (Potential memory leak).", + obj->IsA()->GetName(),obj->GetName()); + } ROOT::DirAutoAdd_t func = old->IsA()->GetDirectoryAutoAdd(); if (func) { func(old,nullptr); diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 57f56133d00cb..4c3ccaa5b82be 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -71,9 +71,11 @@ of a main program creating an interactive version is shown below: #include #include "RConfigure.h" #include "RConfigOptions.h" +#include #include #include #include +#include #include #include #ifdef WIN32 @@ -471,6 +473,63 @@ namespace Internal { return isImplicitMTEnabled; } + //////////////////////////////////////////////////////////////////////////////// + /// \brief Test if objects such as TH1-derived classes should be implicitly + /// registered to gDirectory. + /// A default can be set in a .rootrc using "Root.ImplicitOwnership: 1" or setting + /// the environment variable "ROOT_IMPLICIT_OWNERSHIP=0". + static std::atomic_bool &IsImplicitOwnershipEnabledImpl() + { + static std::atomic_bool initCompleted = false; + static std::atomic_bool implicitOwnership = true; + + if (!initCompleted.load(std::memory_order_relaxed)) { + R__LOCKGUARD(gROOTMutex); + // test again, because another thread might have raced us here + if (!initCompleted) { + std::stringstream infoMessage; + + if (gEnv) { + const auto desiredValue = gEnv->GetValue("Root.ImplicitOwnership", -1); + if (desiredValue == 0) { + implicitOwnership = false; + infoMessage << "Implicit object ownership switched off in rootrc\n"; + } else if (desiredValue == 1) { + implicitOwnership = true; + infoMessage << "Implicit object ownership switched on in rootrc\n"; + } else if (desiredValue != -1) { + Error("TROOT", "Root.ImplicitOwnership should be 0 or 1"); + } + } + + if (auto env = gSystem->Getenv("ROOT_IMPLICIT_OWNERSHIP"); env) { + int desiredValue = -1; + try { + desiredValue = std::stoi(env); + } catch (std::invalid_argument &e) { + Error("TROOT", "ROOT_IMPLICIT_OWNERSHIP should be 0 or 1"); + } + if (desiredValue == 0) { + implicitOwnership = false; + infoMessage << "Implicit object ownership switched off using ROOT_IMPLICIT_OWNERSHIP\n"; + } else if (desiredValue == 1) { + implicitOwnership = true; + infoMessage << "Implicit object ownership switched on using ROOT_IMPLICIT_OWNERSHIP\n"; + } else { + Error("TROOT", "ROOT_IMPLICIT_OWNERSHIP should be 0 or 1"); + } + } + + if (!infoMessage.str().empty()) { + Info("TROOT", "%s", infoMessage.str().c_str()); + } + + initCompleted = true; + } + } + + return implicitOwnership; + } } // end of Internal sub namespace // back to ROOT namespace @@ -616,6 +675,63 @@ namespace Internal { return 0; #endif } + + namespace Experimental { + //////////////////////////////////////////////////////////////////////////////// + /// \brief Switch ROOT's object ownership model to ROOT 6 mode. + /// + /// In ROOT 6 mode, ROOT will implicitly assign ownership of histograms or TTrees + /// to the current \ref gDirectory, for example to the last TFile that was opened. + /// \code{.cpp} + /// TFile file(...); + /// TTree* tree = new TTree(...); + /// TH1D* histo = new TH1D(...); + /// file.Write(); // Both tree and histogram are in the file now + /// \endcode + /// + /// In ROOT 7 mode, these objects won't register themselves to the current gDirectory, + /// so they are fully owned by the user. To write these to files, the user needs to do + /// one of the following: + /// - Explicitly transfer ownership: + /// \code{.cpp} + /// TFile file(...); + /// TTree* tree = new TTree(...); + /// tree->SetDirectory(&file); + /// \endcode + /// - Keep ownership of the object, but write explicitly: + /// \code{.cpp} + /// TFile file(...); + /// std::unique_ptr histo{new TH1D(...)}; + /// file.WriteObject(histo.get(), "HistogramName"); + /// file.Close(); + /// // histo is still valid + /// \endcode + /// + /// \note This setting has higher priority than TH1::AddDirectoryStatus() and TDirectory::AddDirectoryStatus(). + /// These two will always evaluate to false if implicit ownership is off. + /// + void EnableImplicitObjectOwnership() + { + ROOT::Internal::IsImplicitOwnershipEnabledImpl() = true; + } + + //////////////////////////////////////////////////////////////////////////////// + /// \brief Switch ROOT's object ownership model to ROOT 7 mode (no ownership). + /// \copydetails ROOT::Experimental::EnableImplicitObjectOwnership() + void DisableImplicitObjectOwnership() + { + ROOT::Internal::IsImplicitOwnershipEnabledImpl() = false; + } + + //////////////////////////////////////////////////////////////////////////////// + /// Test whether the current directory should take ownership of objects such as + /// TH1-derived classes, TTree, TEntryList etc. + /// \copydetails ROOT::Experimental::EnableImplicitObjectOwnership() + bool IsImplicitObjectOwnershipEnabled() + { + return ROOT::Internal::IsImplicitOwnershipEnabledImpl(); + } + } // namespace Experimental } // end of ROOT namespace TROOT *ROOT::Internal::gROOTLocal = ROOT::GetROOT(); @@ -2832,7 +2948,7 @@ void TROOT::SetBatch(Bool_t batch) /// interactive mode. /// - "server:port": turns the web display into server mode with specified port. Web widgets will not be displayed, /// only text message with window URL will be printed on standard output -/// +/// /// \note See more details related to webdisplay on RWebWindowsManager::ShowWindow void TROOT::SetWebDisplay(const char *webdisplay) diff --git a/hist/hist/src/TH1.cxx b/hist/hist/src/TH1.cxx index 83720aeffe471..b31507c6e2219 100644 --- a/hist/hist/src/TH1.cxx +++ b/hist/hist/src/TH1.cxx @@ -742,11 +742,13 @@ TH1::TH1(const char *name,const char *title,Int_t nbins,const Double_t *xbins) } //////////////////////////////////////////////////////////////////////////////// -/// Static function: cannot be inlined on Windows/NT. +/// Check whether TH1-derived classes should register themselves to the current gDirectory. +/// \note ROOT::Experimental::IsImplicitObjectOwnershipEnabled() might lead to this +/// setting being always off, since it has higher precedence. Bool_t TH1::AddDirectoryStatus() { - return fgAddDirectory; + return ROOT::Experimental::IsImplicitObjectOwnershipEnabled() && fgAddDirectory; } //////////////////////////////////////////////////////////////////////////////// @@ -1275,16 +1277,19 @@ Bool_t TH1::Add(const TH1 *h1, const TH1 *h2, Double_t c1, Double_t c2) } //////////////////////////////////////////////////////////////////////////////// -/// Sets the flag controlling the automatic add of histograms in memory +/// Sets the flag controlling the automatic add of histograms in memory. /// /// By default (fAddDirectory = kTRUE), histograms are automatically added -/// to the list of objects in memory. +/// to the current directory (gDirectory). /// Note that one histogram can be removed from its support directory /// by calling h->SetDirectory(nullptr) or h->SetDirectory(dir) to add it /// to the list of objects in the directory dir. /// -/// NOTE that this is a static function. To call it, use; -/// TH1::AddDirectory +/// This is a static function. To call it, use `TH1::AddDirectory` +/// +/// \note When ROOT::Experimental::IsImplicitOwnershipEnabled() is off, AddDirectory is +/// without effect. +/// void TH1::AddDirectory(Bool_t add) { @@ -2750,7 +2755,7 @@ void TH1::Copy(TObject &obj) const // will be added to gDirectory independently of the fDirectory stored. // and if the AddDirectoryStatus() is false it will not be added to // any directory (fDirectory = nullptr) - if (fgAddDirectory && gDirectory) { + if (AddDirectoryStatus() && gDirectory) { gDirectory->Append(&obj); ((TH1&)obj).fFunctions->UseRWLock(); ((TH1&)obj).fDirectory = gDirectory; @@ -2806,16 +2811,14 @@ TObject* TH1::Clone(const char* newname) const } //////////////////////////////////////////////////////////////////////////////// -/// Perform the automatic addition of the histogram to the given directory +/// Callback to perform the automatic addition of the histogram to the given directory. /// -/// Note this function is called in place when the semantic requires -/// this object to be added to a directory (I.e. when being read from -/// a TKey or being Cloned) +/// This callback is used to register a histogram to the current directory when a TKey +/// is read or an object is being Cloned. void TH1::DirectoryAutoAdd(TDirectory *dir) { - Bool_t addStatus = TH1::AddDirectoryStatus(); - if (addStatus) { + if (fgAddDirectory) { SetDirectory(dir); if (dir) { ResetBit(kCanDelete); diff --git a/hist/hist/test/test_TH1.cxx b/hist/hist/test/test_TH1.cxx index b1fba0682f706..6c42fda82b228 100644 --- a/hist/hist/test/test_TH1.cxx +++ b/hist/hist/test/test_TH1.cxx @@ -170,8 +170,54 @@ TEST(TH1, Normalize) EXPECT_FLOAT_EQ(v2.GetMaximum(), 7.9999990); } +TEST(TH1, RegistrationToTDirectory_ImplicitOwnershipOff) +{ + const bool ownershipEnabledBefore = ROOT::Experimental::IsImplicitObjectOwnershipEnabled(); + ROOT::Experimental::DisableImplicitObjectOwnership(); + + TH1D histo1("histo1", "Test Histogram", 10, 0, 10); + auto histo2 = std::make_unique("histo2", "Test Histogram", 10, 0, 10); + TH1D *histo3 = new TH1D("histo3", "Test Histogram", 10, 0, 10); + + { + TDirectory dir("dir", "Test Directory"); + histo3->SetDirectory(&dir); + + dir.cd(); + + TH1D histo4("histo4", "Test Histogram", 10, 0, 10); + auto histo5 = std::make_unique("histo5", "Test Histogram", 10, 0, 10); + + EXPECT_EQ(dir.GetList()->GetSize(), 1); + EXPECT_EQ(dir.Get("histo3"), histo3); + + EXPECT_EQ(histo1.GetDirectory(), nullptr); + EXPECT_EQ(histo2->GetDirectory(), nullptr); + EXPECT_EQ(histo3->GetDirectory(), &dir); + EXPECT_EQ(histo4.GetDirectory(), nullptr); + EXPECT_EQ(histo5->GetDirectory(), nullptr); + + histo5.reset(); + + EXPECT_EQ(dir.GetList()->GetSize(), 1); + EXPECT_EQ(dir.Get("histo3"), histo3); + } + + EXPECT_STREQ(histo1.GetName(), "histo1"); + EXPECT_STREQ(histo2->GetName(), "histo2"); + + EXPECT_EQ(histo1.GetDirectory(), nullptr); + EXPECT_EQ(histo2->GetDirectory(), nullptr); + + if (ownershipEnabledBefore) + ROOT::Experimental::EnableImplicitObjectOwnership(); +} + TEST(TH1, RegistrationToTDirectory_ImplicitOwnershipOn) { + const bool ownershipDisabledBefore = ! ROOT::Experimental::IsImplicitObjectOwnershipEnabled(); + ROOT::Experimental::EnableImplicitObjectOwnership(); + TH1D histo1("histo1", "Test Histogram", 10, 0, 10); auto histo2 = std::make_unique("histo2", "Test Histogram", 10, 0, 10); TH1D *histo3 = new TH1D("histo3", "Test Histogram", 10, 0, 10); @@ -213,6 +259,8 @@ TEST(TH1, RegistrationToTDirectory_ImplicitOwnershipOn) EXPECT_EQ(histo1.GetDirectory(), gROOT); EXPECT_EQ(histo2->GetDirectory(), gROOT); + + if (ownershipDisabledBefore) ROOT::Experimental::DisableImplicitObjectOwnership(); } TEST(TAxis, BinComputation_FPAccuracy) diff --git a/io/io/test/TFileMergerTests.cxx b/io/io/test/TFileMergerTests.cxx index 3d790f80f13a2..b32c6e321c3af 100644 --- a/io/io/test/TFileMergerTests.cxx +++ b/io/io/test/TFileMergerTests.cxx @@ -91,6 +91,8 @@ TEST(TFileMerger, MergeSingleOnlyListed) auto hist2 = new TH1F("hist2", "hist2", 1 , 0 , 2); auto hist3 = new TH1F("hist3", "hist3", 1 , 0 , 2); auto hist4 = new TH1F("hist4", "hist4", 1 , 0 , 2); + for (auto hist : {hist1, hist2, hist3, hist4}) + hist->SetDirectory(&a); hist1->Fill(1); hist2->Fill(1); hist2->Fill(2); hist3->Fill(1); hist3->Fill(1); hist3->Fill(1); diff --git a/io/io/test/rfile.cxx b/io/io/test/rfile.cxx index 16f38fdbab3a8..0c689e484345d 100644 --- a/io/io/test/rfile.cxx +++ b/io/io/test/rfile.cxx @@ -132,9 +132,10 @@ TEST(RFile, CheckNoAutoRegistrationWrite) EXPECT_EQ(gDirectory, gROOT); auto hist = std::make_unique("hist", "", 100, -10, 10); file->Put("hist", *hist); - EXPECT_EQ(hist->GetDirectory(), gROOT); + TDirectory const *expectedDir = ROOT::Experimental::IsImplicitObjectOwnershipEnabled() ? gROOT : nullptr; + EXPECT_EQ(hist->GetDirectory(), expectedDir); file->Close(); - EXPECT_EQ(hist->GetDirectory(), gROOT); + EXPECT_EQ(hist->GetDirectory(), expectedDir); hist.reset(); // no double free should happen when ROOT exits } diff --git a/roofit/roofitcore/src/RooPlot.cxx b/roofit/roofitcore/src/RooPlot.cxx index 84db698f46985..ec30b3460dac7 100644 --- a/roofit/roofitcore/src/RooPlot.cxx +++ b/roofit/roofitcore/src/RooPlot.cxx @@ -56,6 +56,7 @@ object onto a one-dimensional plot. #include "TH1D.h" #include "TBrowser.h" #include "TVirtualPad.h" +#include "TROOT.h" #include "TAttLine.h" #include "TAttFill.h" @@ -73,7 +74,10 @@ object onto a one-dimensional plot. bool RooPlot::_addDirStatus = true ; -bool RooPlot::addDirectoryStatus() { return _addDirStatus; } +bool RooPlot::addDirectoryStatus() +{ + return ROOT::Experimental::IsImplicitObjectOwnershipEnabled() && _addDirStatus; +} bool RooPlot::setAddDirectoryStatus(bool flag) { bool ret = flag ; _addDirStatus = flag ; return ret ; } diff --git a/roottest/scripts/custom_diff.py b/roottest/scripts/custom_diff.py index 10f5e1e9c97bf..db34b79204193 100755 --- a/roottest/scripts/custom_diff.py +++ b/roottest/scripts/custom_diff.py @@ -32,6 +32,7 @@ def __init__(self): r"^Info in <\w+::ACLiC>: creating shared library", # Compiled macros r"^In file included from input_line", # Wrapper input line r"^[:space:]*$", # Lines which are empty apart from spaces + r'^Info in : Implicit object ownership', # ROOT 7 mode ] ] diff --git a/test/stress.cxx b/test/stress.cxx index 04bd71f63209c..ca9c94d2a1f55 100644 --- a/test/stress.cxx +++ b/test/stress.cxx @@ -1,5 +1,6 @@ // @(#)root/test:$Id$ // Author: Rene Brun 05/11/98 +// clang-format off ///////////////////////////////////////////////////////////////// // @@ -574,9 +575,11 @@ void stress6() snprintf(hname,20,"h%d_%dN",i,j); snprintf(htitle,80,"hist for counter:%d in plane:%d North",j,i); hn[j] = new TH1S(hname,htitle,100,0,100); + hn[j]->SetDirectory(cdplane); snprintf(hname,20,"h%d_%dS",i,j); snprintf(htitle,80,"hist for counter:%d in plane:%d South",j,i); hs[j] = new TH1S(hname,htitle,100,0,100); + hs[j]->SetDirectory(cdplane); } // fill counter histograms randomly for (k=0;k<10000;k++) { @@ -668,6 +671,7 @@ void stress7() cutg->SetPoint(7,-1.27161,1.01523); cutg->SetPoint(8,-1.75713,2.46193); TH2F *hpxpy = new TH2F("hpxpy","px vx py with cutg",40,-4,4,40,-4,4); + hpxpy->SetDirectory(&f); ntuple->Draw("px:py>>hpxpy","cutg","goff"); Int_t npxpy = (Int_t)hpxpy->GetEntries(); Int_t npxpyGood = 27918; @@ -1015,40 +1019,40 @@ void stress9tree(TTree *tree, Int_t realTestNum) //We make clones of the generated histograms //We set new names and reset the clones. //We want to have identical histogram limits - TH1F *bNtrack = (TH1F*)hNtrack->Clone(); bNtrack->SetName("bNtrack"); bNtrack->Reset(); - TH1F *bNseg = (TH1F*)hNseg->Clone(); bNseg->SetName("bNseg"); bNseg->Reset(); - TH1F *bTemp = (TH1F*)hTemp->Clone(); bTemp->SetName("bTemp"); bTemp->Reset(); - TH1F *bHmean = (TH1F*)hHmean->Clone(); bHmean->SetName("bHmean"); bHmean->Reset(); - TH1F *bPx = (TH1F*)hPx->Clone(); bPx->SetName("bPx"); bPx->Reset(); - TH1F *bPy = (TH1F*)hPy->Clone(); bPy->SetName("bPy"); bPy->Reset(); - TH1F *bPz = (TH1F*)hPz->Clone(); bPz->SetName("bPz"); bPz->Reset(); - TH1F *bRandom = (TH1F*)hRandom->Clone(); bRandom->SetName("bRandom"); bRandom->Reset(); - TH1F *bMass2 = (TH1F*)hMass2->Clone(); bMass2->SetName("bMass2"); bMass2->Reset(); - TH1F *bBx = (TH1F*)hBx->Clone(); bBx->SetName("bBx"); bBx->Reset(); - TH1F *bBy = (TH1F*)hBy->Clone(); bBy->SetName("bBy"); bBy->Reset(); - TH1F *bXfirst = (TH1F*)hXfirst->Clone(); bXfirst->SetName("bXfirst"); bXfirst->Reset(); - TH1F *bYfirst = (TH1F*)hYfirst->Clone(); bYfirst->SetName("bYfirst"); bYfirst->Reset(); - TH1F *bZfirst = (TH1F*)hZfirst->Clone(); bZfirst->SetName("bZfirst"); bZfirst->Reset(); - TH1F *bXlast = (TH1F*)hXlast->Clone(); bXlast->SetName("bXlast"); bXlast->Reset(); - TH1F *bYlast = (TH1F*)hYlast->Clone(); bYlast->SetName("bYlast"); bYlast->Reset(); - TH1F *bZlast = (TH1F*)hZlast->Clone(); bZlast->SetName("bZlast"); bZlast->Reset(); - TH1F *bCharge = (TH1F*)hCharge->Clone(); bCharge->SetName("bCharge"); bCharge->Reset(); - TH1F *bNpoint = (TH1F*)hNpoint->Clone(); bNpoint->SetName("bNpoint"); bNpoint->Reset(); - TH1F *bValid = (TH1F*)hValid->Clone(); bValid->SetName("bValid"); bValid->Reset(); - - TH1F *bFullMatrix =(TH1F*)hFullMatrix->Clone(); bFullMatrix->SetName("bFullMatrix"); bFullMatrix->Reset(); - TH1F *bColMatrix = (TH1F*)hColMatrix->Clone(); bColMatrix->SetName("bColMatrix"); bColMatrix->Reset(); - TH1F *bRowMatrix = (TH1F*)hRowMatrix->Clone(); bRowMatrix->SetName("bRowMatrix"); bRowMatrix->Reset(); - TH1F *bCellMatrix = (TH1F*)hCellMatrix->Clone(); bCellMatrix->SetName("bCellMatrix"); bCellMatrix->Reset(); - TH1F *bFullOper = (TH1F*)hFullOper->Clone(); bFullOper->SetName("bFullOper"); bFullOper->Reset(); - TH1F *bCellOper = (TH1F*)hCellOper->Clone(); bCellOper->SetName("bCellOper"); bCellOper->Reset(); - TH1F *bColOper = (TH1F*)hColOper->Clone(); bColOper->SetName("bColOper"); bColOper->Reset(); - TH1F *bRowOper = (TH1F*)hRowOper->Clone(); bRowOper->SetName("bRowOper"); bRowOper->Reset(); - TH1F *bMatchRowOper = (TH1F*)hMatchRowOper->Clone(); bMatchRowOper->SetName("bMatchRowOper"); bMatchRowOper->Reset(); - TH1F *bMatchColOper = (TH1F*)hMatchColOper->Clone(); bMatchColOper->SetName("bMatchColOper"); bMatchColOper->Reset(); - TH1F *bRowMatOper = (TH1F*)hRowMatOper->Clone(); bRowMatOper->SetName("bRowMatOper"); bRowMatOper->Reset(); - TH1F *bMatchDiffOper= (TH1F*)hMatchDiffOper->Clone(); bMatchDiffOper->SetName("bMatchDiffOper"); bMatchDiffOper->Reset(); - TH1F *bFullOper2 = (TH1F*)hFullOper2->Clone(); bFullOper2->SetName("bFullOper2"); bFullOper2->Reset(); + TH1F *bNtrack = (TH1F*)hNtrack->Clone("bNtrack"); bNtrack->SetDirectory(hfile); bNtrack->Reset(); + TH1F *bNseg = (TH1F*)hNseg->Clone("bNseg"); bNseg->SetDirectory(hfile); bNseg->Reset(); + TH1F *bTemp = (TH1F*)hTemp->Clone("bTemp"); bTemp->SetDirectory(hfile); bTemp->Reset(); + TH1F *bHmean = (TH1F*)hHmean->Clone("bHmean"); bHmean->SetDirectory(hfile); bHmean->Reset(); + TH1F *bPx = (TH1F*)hPx->Clone("bPx"); bPx->SetDirectory(hfile); bPx->Reset(); + TH1F *bPy = (TH1F*)hPy->Clone("bPy"); bPy->SetDirectory(hfile); bPy->Reset(); + TH1F *bPz = (TH1F*)hPz->Clone("bPz"); bPz->SetDirectory(hfile); bPz->Reset(); + TH1F *bRandom = (TH1F*)hRandom->Clone("bRandom"); bRandom->SetDirectory(hfile); bRandom->Reset(); + TH1F *bMass2 = (TH1F*)hMass2->Clone("bMass2"); bMass2->SetDirectory(hfile); bMass2->Reset(); + TH1F *bBx = (TH1F*)hBx->Clone("bBx"); bBx->SetDirectory(hfile); bBx->Reset(); + TH1F *bBy = (TH1F*)hBy->Clone("bBy"); bBy->SetDirectory(hfile); bBy->Reset(); + TH1F *bXfirst = (TH1F*)hXfirst->Clone("bXfirst"); bXfirst->SetDirectory(hfile); bXfirst->Reset(); + TH1F *bYfirst = (TH1F*)hYfirst->Clone("bYfirst"); bYfirst->SetDirectory(hfile); bYfirst->Reset(); + TH1F *bZfirst = (TH1F*)hZfirst->Clone("bZfirst"); bZfirst->SetDirectory(hfile); bZfirst->Reset(); + TH1F *bXlast = (TH1F*)hXlast->Clone("bXlast"); bXlast->SetDirectory(hfile); bXlast->Reset(); + TH1F *bYlast = (TH1F*)hYlast->Clone("bYlast"); bYlast->SetDirectory(hfile); bYlast->Reset(); + TH1F *bZlast = (TH1F*)hZlast->Clone("bZlast"); bZlast->SetDirectory(hfile); bZlast->Reset(); + TH1F *bCharge = (TH1F*)hCharge->Clone("bCharge"); bCharge->SetDirectory(hfile); bCharge->Reset(); + TH1F *bNpoint = (TH1F*)hNpoint->Clone("bNpoint"); bNpoint->SetDirectory(hfile); bNpoint->Reset(); + TH1F *bValid = (TH1F*)hValid->Clone("bValid"); bValid->SetDirectory(hfile); bValid->Reset(); + + TH1F *bFullMatrix =(TH1F*)hFullMatrix->Clone("bFullMatrix"); bFullMatrix->SetDirectory(hfile); bFullMatrix->Reset(); + TH1F *bColMatrix = (TH1F*)hColMatrix->Clone("bColMatrix"); bColMatrix->SetDirectory(hfile); bColMatrix->Reset(); + TH1F *bRowMatrix = (TH1F*)hRowMatrix->Clone("bRowMatrix"); bRowMatrix->SetDirectory(hfile); bRowMatrix->Reset(); + TH1F *bCellMatrix = (TH1F*)hCellMatrix->Clone("bCellMatrix"); bCellMatrix->SetDirectory(hfile); bCellMatrix->Reset(); + TH1F *bFullOper = (TH1F*)hFullOper->Clone("bFullOper"); bFullOper->SetDirectory(hfile); bFullOper->Reset(); + TH1F *bCellOper = (TH1F*)hCellOper->Clone("bCellOper"); bCellOper->SetDirectory(hfile); bCellOper->Reset(); + TH1F *bColOper = (TH1F*)hColOper->Clone("bColOper"); bColOper->SetDirectory(hfile); bColOper->Reset(); + TH1F *bRowOper = (TH1F*)hRowOper->Clone("bRowOper"); bRowOper->SetDirectory(hfile); bRowOper->Reset(); + TH1F *bMatchRowOper = (TH1F*)hMatchRowOper->Clone("bMatchRowOper"); bMatchRowOper->SetDirectory(hfile); bMatchRowOper->Reset(); + TH1F *bMatchColOper = (TH1F*)hMatchColOper->Clone("bMatchColOper"); bMatchColOper->SetDirectory(hfile); bMatchColOper->Reset(); + TH1F *bRowMatOper = (TH1F*)hRowMatOper->Clone("bRowMatOper"); bRowMatOper->SetDirectory(hfile); bRowMatOper->Reset(); + TH1F *bMatchDiffOper= (TH1F*)hMatchDiffOper->Clone("bMatchDiffOper");bMatchDiffOper->SetDirectory(hfile); bMatchDiffOper->Reset(); + TH1F *bFullOper2 = (TH1F*)hFullOper2->Clone("bFullOper2"); bFullOper2->SetDirectory(hfile); bFullOper2->Reset(); // Loop with user code on all events and fill the b histograms // The code below should produce identical results to the tree->Draw above @@ -1336,9 +1340,16 @@ void stress12(Int_t testid) if (strcmp(key->GetClassName(),"TH1F")) continue; //may be a TList of TStreamerInfo h9 = (TH1F*)f9.Get(key->GetName()); h11 = (TH1F*)f11.Get(key->GetName()); - if (h9 == 0 || h11 == 0) continue; + if (h9 == 0 || h11 == 0) { + std::cerr << "Missing " << key->GetName(); + if (!h9) std::cerr << " in stress_test9.root"; + if (!h11) std::cerr << " in stress_test11.root"; + std::cerr << "\n"; + continue; + } comp = HistCompare(h9,h11); if (comp == 0) ngood++; + else std::cerr << key->GetName() << " not equal\n"; } ntotin += f9.GetBytesRead(); ntotin += f11.GetBytesRead(); diff --git a/test/stressGraphics.cxx b/test/stressGraphics.cxx index 2acfd69d0fd64..e83514d0a927e 100644 --- a/test/stressGraphics.cxx +++ b/test/stressGraphics.cxx @@ -3812,6 +3812,8 @@ void zoomfit() void hbars() { TCanvas *C = StartTest(700,800); + TDirectory dir("hbars_dir", "Directory for the hbars test"); + TDirectory::TContext dirCtx{&dir}; TTree *T = (TTree*)gCernstaff->Get("T"); T->SetFillColor(45); @@ -3825,9 +3827,10 @@ void hbars() //vertical bar chart C->cd(2); gPad->SetGrid(); gPad->SetFrameFillColor(33); T->Draw("Division>>hDiv","","goff"); - TH1F *hDiv = (TH1F*)gDirectory->Get("hDiv"); + TH1F *hDiv = (TH1F*)dir.Get("hDiv"); hDiv->SetStats(0); TH1F *hDivFR = (TH1F*)hDiv->Clone("hDivFR"); + hDivFR->SetDirectory(&dir); T->Draw("Division>>hDivFR","Nation==\"FR\"","goff"); hDiv->SetBarWidth(0.45); hDiv->SetBarOffset(0.1); diff --git a/tree/treeplayer/src/TSelectorDraw.cxx b/tree/treeplayer/src/TSelectorDraw.cxx index 2dda2a80a2596..8aa71aec11512 100644 --- a/tree/treeplayer/src/TSelectorDraw.cxx +++ b/tree/treeplayer/src/TSelectorDraw.cxx @@ -578,6 +578,7 @@ void TSelectorDraw::Begin(TTree *tree) } else { hist = new TH1D(hname, htitle.Data(), fNbins[0], fVmin[0], fVmax[0]); } + hist->SetDirectory(gDirectory); hist->SetLineColor(fTree->GetLineColor()); hist->SetLineWidth(fTree->GetLineWidth()); hist->SetLineStyle(fTree->GetLineStyle()); @@ -661,6 +662,7 @@ void TSelectorDraw::Begin(TTree *tree) } else { hp = new TProfile(hname, htitle.Data(), fNbins[1], fVmin[1], fVmax[1], ""); } + hp->SetDirectory(gDirectory); if (!hkeep) { hp->SetBit(kCanDelete); if (!opt.Contains("goff")) hp->SetDirectory(nullptr); @@ -689,6 +691,7 @@ void TSelectorDraw::Begin(TTree *tree) } else { h2 = new TH2D(hname, htitle.Data(), fNbins[1], fVmin[1], fVmax[1], fNbins[0], fVmin[0], fVmax[0]); } + h2->SetDirectory(gDirectory); h2->SetLineColor(fTree->GetLineColor()); h2->SetLineWidth(fTree->GetLineWidth()); h2->SetLineStyle(fTree->GetLineStyle()); @@ -803,6 +806,7 @@ void TSelectorDraw::Begin(TTree *tree) } else { hp = new TProfile2D(hname, htitle.Data(), fNbins[2], fVmin[2], fVmax[2], fNbins[1], fVmin[1], fVmax[1], ""); } + hp->SetDirectory(gDirectory); if (!hkeep) { hp->SetBit(kCanDelete); if (!opt.Contains("goff")) hp->SetDirectory(nullptr); @@ -826,6 +830,7 @@ void TSelectorDraw::Begin(TTree *tree) h2 = (TH2F*)fOldHistogram; } else { h2 = new TH2F(hname, htitle.Data(), fNbins[1], fVmin[1], fVmax[1], fNbins[0], fVmin[0], fVmax[0]); + h2->SetDirectory(gDirectory); h2->SetLineColor(fTree->GetLineColor()); h2->SetLineWidth(fTree->GetLineWidth()); h2->SetLineStyle(fTree->GetLineStyle()); @@ -859,6 +864,7 @@ void TSelectorDraw::Begin(TTree *tree) } else { h3 = new TH3D(hname, htitle.Data(), fNbins[2], fVmin[2], fVmax[2], fNbins[1], fVmin[1], fVmax[1], fNbins[0], fVmin[0], fVmax[0]); } + h3->SetDirectory(gDirectory); h3->SetLineColor(fTree->GetLineColor()); h3->SetLineWidth(fTree->GetLineWidth()); h3->SetLineStyle(fTree->GetLineStyle()); diff --git a/tutorials/io/tfile_context_manager.py b/tutorials/io/tfile_context_manager.py index b2b95fe625355..0ed204333959e 100644 --- a/tutorials/io/tfile_context_manager.py +++ b/tutorials/io/tfile_context_manager.py @@ -14,10 +14,14 @@ import ROOT from ROOT import TFile, gROOT -# By default, objects of some ROOT types such as `TH1` and its derived types +# By default, in ROOT 6, objects of some ROOT types such as `TH1` and its derived types # are automatically attached to a ROOT.TDirectory when they are created. -# Specifically, at any given point of a ROOT application, the ROOT.gDirectory -# object tells which is the current directory where objects will be attached to. +# Objects are attached to the current directory denoted by ROOT.gDirectory. +# For ROOT 7, the default will be *not* to attach objects to ROOT.gDirectory, except for +# cases where the documentation states it explicitly, such as TTree. +# In ROOT 6, implicit ownership off can be tested by setting ROOT.Experimental.DisableImplicitObjectOwnership() +# (the reverse is EnableImplicitObjectOwnership()). + # The next line will print 'PyROOT' as the name of the current directory. # That is the global directory created when using ROOT from Python, which is # the ROOT.gROOT object. @@ -25,7 +29,7 @@ # We can check to which directory a newly created histogram is attached. histo_1 = ROOT.TH1F("histo_1", "histo_1", 10, 0, 10) -print("Histogram '{}' is attached to: '{}'.\n".format(histo_1.GetName(), histo_1.GetDirectory().GetName())) +print("Histogram '{}' is attached to: '{}'.\n".format(histo_1.GetName(), "Nothing" if not histo_1.GetDirectory() else histo_1.GetDirectory().GetName())) # For quick saving and forgetting of objects into ROOT files, it is possible to # open a TFile as a Python context manager. In the context, objects can be @@ -37,8 +41,11 @@ histo_2 = ROOT.TH1F("histo_2", "histo_2", 10, 0, 10) # Inside the context, the current directory is the open file print("Current directory: '{}'.\n".format(ROOT.gDirectory.GetName())) - # And the created histogram is automatically attached to the file - print("Histogram '{}' is attached to: '{}'.\n".format(histo_2.GetName(), histo_2.GetDirectory().GetName())) + # In ROOT 6, the created histogram is automatically attached to the file + print("Histogram '{}' is attached to: '{}'.\n".format(histo_2.GetName(), histo_2.GetDirectory().GetName() if histo_2.GetDirectory() else "Nothing")) + # In ROOT 7, this can be done explicitly: + histo_2.SetDirectory(f) + print("After setting the directory, histogram '{}' is attached to: '{}'.\n".format(histo_2.GetName(), histo_2.GetDirectory().GetName())) # Before exiting the context, objects can be written to the file f.WriteObject(histo_2, "my_histogram")