Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/framework/core/src/TRestDataSet.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,7 @@ void TRestDataSet::Export(const std::string& filename, std::vector<std::string>
fDataFrame.Snapshot("AnalysisTree", filename);

TFile* f = TFile::Open(filename.c_str(), "UPDATE");
TRestTools::PreserveStreamerInfos(f);
std::string name = this->GetName();
if (name.empty()) name = "mock";
this->Write(name.c_str());
Expand Down
1 change: 1 addition & 0 deletions source/framework/core/src/TRestProcessRunner.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,7 @@ void TRestProcessRunner::FillThreadEventFunc(TRestThread* t) {
fRunInfo->SetNFilesSplit(fNFilesSplit);
if (fOutputDataFile->GetName() != fOutputDataFileName) {
auto Mainfile = std::unique_ptr<TFile>{TFile::Open(fOutputDataFileName, "update")};
TRestTools::PreserveStreamerInfos(Mainfile.get());
WriteProcessesMetadata();
Mainfile->Write(0, TObject::kOverwrite);
Mainfile->Close();
Expand Down
5 changes: 5 additions & 0 deletions source/framework/core/src/TRestRun.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,10 @@ TFile* TRestRun::MergeToOutputFile(vector<string> filenames, string outputfilena

// write metadata into the output file
fOutputFile = new TFile(fOutputFileName, "update");
// Without this, closing the file would keep only the StreamerInfos of the classes
// streamed during this metadata session, wiping the event-class StreamerInfos
// written by the merge above (see TRestTools::PreserveStreamerInfos)
TRestTools::PreserveStreamerInfos(fOutputFile);
RESTDebug << "TRestRun::FormOutputFile. Calling WriteWithDataBase()" << RESTendl;
this->WriteWithDataBase();

Expand Down Expand Up @@ -1060,6 +1064,7 @@ TFile* TRestRun::UpdateOutputFile() {
if (fOutputFile->IsOpen()) {
fOutputFile->ReOpen("update");
}
TRestTools::PreserveStreamerInfos(fOutputFile);

fOutputFile->cd();

Expand Down
4 changes: 4 additions & 0 deletions source/framework/tools/inc/TRestTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ EXTERN_DEF std::string REST_USER_PATH;
EXTERN_DEF std::string REST_TMP_PATH;
EXTERN_DEF std::map<std::string, std::string> REST_ARGS;

class TFile;

/// A generic class with useful static methods.
class TRestTools {
public:
Expand Down Expand Up @@ -115,6 +117,8 @@ class TRestTools {
template <typename T>
static int ExportBinaryTable(std::string fname, std::vector<std::vector<T>>& data);

static void PreserveStreamerInfos(TFile* file);

static Int_t isValidFile(const std::string& path);
static bool fileExists(const std::string& filename);
static bool isRootFile(const std::string& filename);
Expand Down
45 changes: 45 additions & 0 deletions source/framework/tools/src/TRestTools.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
///
#include "TRestTools.h"

#include <TArrayC.h>
#include <TClass.h>
#include <TFile.h>
#include <TKey.h>
#include <TStreamerInfo.h>
#include <TSystem.h>
#include <TUrl.h>

Expand Down Expand Up @@ -713,6 +715,49 @@ int TRestTools::ReadCSVFile(std::string fName, std::vector<std::vector<Float_t>>
return ReadASCIITable(fName, data, skipLines, ",");
}

///////////////////////////////////////////////
/// \brief Re-tag the StreamerInfos already stored in a file opened in UPDATE mode,
/// so that they survive the session.
///
/// When a ROOT file is closed, TFile::WriteStreamerInfo() REPLACES the StreamerInfo
/// record of the file, keeping only the infos of the classes streamed during that
/// session. An UPDATE session that only adds metadata (e.g. the reopen done by
/// TRestRun::MergeToOutputFile after merging the threads' output) therefore wipes
/// the StreamerInfos of the event classes written earlier. Without them, ROOT
/// cannot apply schema evolution when the class definitions change, and old files
/// become unreadable (see rest-for-physics/detectorlib#125).
///
/// Calling this method right after opening a file in UPDATE mode marks every
/// StreamerInfo already present in the file to be written out again on close.
///
void TRestTools::PreserveStreamerInfos(TFile* file) {
if (file == nullptr || !file->IsOpen() || !file->IsWritable()) return;
TArrayC* classIndex = file->GetClassIndex();
if (classIndex == nullptr) return;

std::unique_ptr<TList> infos(file->GetStreamerInfoList());
if (infos == nullptr) return;
infos->SetOwner(kTRUE);

TIter next(infos.get());
TObject* obj;
while ((obj = next())) {
auto info = dynamic_cast<TStreamerInfo*>(obj);
if (info == nullptr) continue;
TClass* cl = TClass::GetClass(info->GetName());
if (cl == nullptr) continue;
auto current = dynamic_cast<TStreamerInfo*>(cl->GetStreamerInfo(info->GetClassVersion()));
if (current == nullptr) continue;
// Same marking as the (deprecated) TStreamerInfo::TagFile: flag the class
// so that TFile::WriteStreamerInfo includes it when the file is closed
const Int_t number = current->GetNumber();
if (number > 0 && number < classIndex->GetSize()) {
classIndex->fArray[number] = 1;
classIndex->fArray[0] = 1;
}
}
}

///////////////////////////////////////////////
/// \brief Returns true if the file with path filename exists.
///
Expand Down
Loading