diff --git a/core/base/inc/TROOT.h b/core/base/inc/TROOT.h index 225bdcf188faa..dbe51ecf2a8df 100644 --- a/core/base/inc/TROOT.h +++ b/core/base/inc/TROOT.h @@ -310,6 +310,8 @@ friend TROOT *ROOT::Internal::GetROOT2(); const FwdDeclArgsToKeepCollection_t& fwdDeclsArgToSkip, const char** classesHeaders, bool hasCxxModule = false); + static void UnRegisterModule(const char* modulename, + void (*triggerFunc)()); TObject *Remove(TObject*); void RemoveClass(TClass *); void Reset(Option_t *option=""); diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 3eb8bd180678f..efe3278abbb56 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -2567,6 +2567,18 @@ void TROOT::RegisterModule(const char* modulename, } } +//////////////////////////////////////////////////////////////////////////////// +/// Called by static dictionary deinitialization to deregister clang modules +/// for headers. Calls TCling::UnRegisterModule(). + +void TROOT::UnRegisterModule(const char* modulename, void (*triggerFunc)()) +{ + if (TROOT::Initialized()) + gCling->UnRegisterModule(modulename, triggerFunc); + // FIXME: Handle the deinitialization properly. ~TCling should call in + // reverse order UnRegisterModule when it is shutting down. +} + //////////////////////////////////////////////////////////////////////////////// /// Remove an object from the in-memory list. /// Since TROOT is global resource, this is lock protected. diff --git a/core/dictgen/src/TModuleGenerator.cxx b/core/dictgen/src/TModuleGenerator.cxx index 896d520a0a1b0..25d4516ca5c4b 100644 --- a/core/dictgen/src/TModuleGenerator.cxx +++ b/core/dictgen/src/TModuleGenerator.cxx @@ -387,6 +387,10 @@ void TModuleGenerator::WriteRegistrationSourceImpl(std::ostream& out, " DictInit() {\n" " TriggerDictionaryInitialization_" << dictName << "_Impl();\n" " }\n" + " ~DictInit() {\n" + " TROOT::UnRegisterModule(\"" << demangledDictName << "\"," + "TriggerDictionaryInitialization_" << dictName << "_Impl);\n" + " }\n" " } __TheDictionaryInitializer;\n" "}\n" "void TriggerDictionaryInitialization_" << dictName << "() {\n" diff --git a/core/meta/inc/TInterpreter.h b/core/meta/inc/TInterpreter.h index 7494a9de5844c..ee0394fd8c702 100644 --- a/core/meta/inc/TInterpreter.h +++ b/core/meta/inc/TInterpreter.h @@ -193,6 +193,8 @@ class TInterpreter : public TNamed { Bool_t lateRegistration = false, Bool_t hasCxxModule = false) = 0; virtual void AddAvailableIndentifiers(TSeqCollection&) = 0; + virtual void UnRegisterModule(const char* /*modulename*/, + void (* /*triggerFunc*/)()) = 0; virtual void RegisterTClassUpdate(TClass *oldcl,DictFuncPtr_t dict) = 0; virtual void UnRegisterTClassUpdate(const TClass *oldcl) = 0; virtual Int_t SetClassSharedLibs(const char *cls, const char *libs) = 0; diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 27834209f552c..88a41b06af90a 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -1695,7 +1695,7 @@ void TCling::RegisterRdictForLoadPCM(const std::string &pcmFileNameFullPath, llv //////////////////////////////////////////////////////////////////////////////// /// Tries to load a PCM from TFile; returns true on success. -void TCling::LoadPCMImpl(TFile &pcmFile) +void TCling::LoadPCMImpl(TFile &pcmFile) const { auto listOfKeys = pcmFile.GetListOfKeys(); @@ -1842,10 +1842,10 @@ void TCling::LoadPCM(std::string pcmFileNameFullPath) llvm::StringRef pcmContent = pendingRdict->second; TMemFile::ZeroCopyView_t range{pcmContent.data(), pcmContent.size()}; std::string RDictFileOpts = pcmFileNameFullPath + "?filetype=pcm"; - TMemFile pcmMemFile(RDictFileOpts.c_str(), range); - + fLoadedRdicts.push_back(new TMemFile(RDictFileOpts.c_str(), range)); + TFile* pcmMemFile = fLoadedRdicts.back(); cling::Interpreter::PushTransactionRAII deserRAII(GetInterpreterImpl()); - LoadPCMImpl(pcmMemFile); + LoadPCMImpl(*pcmMemFile); fPendingRdicts.erase(pendingRdict); return; @@ -1865,8 +1865,55 @@ void TCling::LoadPCM(std::string pcmFileNameFullPath) Fatal("LoadPCM", "The file %s is not a ROOT as was expected\n", pcmFileName.Data()); return; } - TFile pcmFile(pcmFileName + "?filetype=pcm", "READ"); - LoadPCMImpl(pcmFile); + fLoadedRdicts.push_back(new TFile(pcmFileName + "?filetype=pcm", "READ")); + TFile* pcmMemFile = fLoadedRdicts.back(); + LoadPCMImpl(*pcmMemFile); +} + +void TCling::UnLoadPCMImpl(const TFile& rdict) +{ + // FIXME: Reverse the effect of LoadPCMImpl. +} + +void TCling::UnLoadPCM(std::string pcmFileNameFullPath) +{ + SuspendAutoLoadingRAII autoloadOff(this); + SuspendAutoParsing autoparseOff(this); + assert(!pcmFileNameFullPath.empty()); + assert(llvm::sys::path::is_absolute(pcmFileNameFullPath)); + + // Easier to work with the ROOT interfaces. + TString pcmFileName = pcmFileNameFullPath; + + // FIXME: @pcanal: how to deinitialize the streamer info factory? + R__InitStreamerInfoFactory(); + + TDirectory::TContext ctxt; + llvm::SaveAndRestore SaveGDebug(gDebug); + if (gDebug > 5) { + gDebug -= 5; + ::Info("TCling::UnLoadPCM", "Loading ROOT PCM %s", pcmFileName.Data()); + } else { + gDebug = 0; + } + + if (llvm::sys::fs::is_symlink_file(pcmFileNameFullPath)) + pcmFileNameFullPath = ROOT::TMetaUtils::GetRealPath(pcmFileNameFullPath); + + TFile* rdictToUnload = nullptr; + for (const auto& rdict : fLoadedRdicts) { + if (rdict->GetName() == pcmFileNameFullPath) { + rdictToUnload = rdict; + break; + } + } + + assert(rdictToUnload && "Rdict not loaded!"); + + // FIXME: Restore the state to fPendingRdicts, where we will need to have + // enough information to create an TMemFile. + + UnLoadPCMImpl(*rdictToUnload); } //______________________________________________________________________________ @@ -1998,6 +2045,20 @@ void TCling::ProcessClassesToUpdate() } } } + +static std::string GetRdictFullPath(const std::string &dyLibName, + const char* modulename) +{ + llvm::SmallString<256> pcmFileNameFullPath(dyLibName); + // The path dyLibName might not be absolute. This can happen if dyLibName + // is linked to an executable in the same folder. + llvm::sys::fs::make_absolute(pcmFileNameFullPath); + llvm::sys::path::remove_filename(pcmFileNameFullPath); + llvm::sys::path::append(pcmFileNameFullPath, + ROOT::TMetaUtils::GetModuleFileName(modulename)); + return pcmFileNameFullPath.str().str(); +} + //////////////////////////////////////////////////////////////////////////////// /// Inject the module named "modulename" into cling; load all headers. /// headers is a 0-terminated array of header files to #include after @@ -2287,16 +2348,8 @@ void TCling::RegisterModule(const char* modulename, } } - if (gIgnoredPCMNames.find(modulename) == gIgnoredPCMNames.end()) { - llvm::SmallString<256> pcmFileNameFullPath(dyLibName); - // The path dyLibName might not be absolute. This can happen if dyLibName - // is linked to an executable in the same folder. - llvm::sys::fs::make_absolute(pcmFileNameFullPath); - llvm::sys::path::remove_filename(pcmFileNameFullPath); - llvm::sys::path::append(pcmFileNameFullPath, - ROOT::TMetaUtils::GetModuleFileName(modulename)); - LoadPCM(pcmFileNameFullPath.str().str()); - } + if (gIgnoredPCMNames.find(modulename) == gIgnoredPCMNames.end()) + LoadPCM(GetRdictFullPath(dyLibName, modulename)); { // scope within which diagnostics are de-activated // For now we disable diagnostics because we saw them already at @@ -2374,6 +2427,24 @@ void TCling::AddAvailableIndentifiers(TSeqCollection& Idents) { } +//////////////////////////////////////////////////////////////////////////////// +/// Clean up after a dictionary was deinitialized (happens on dlclose). +/// + +void TCling::UnRegisterModule(const char* modulename, void (*triggerFunc)()) +{ + std::string dyLibName = cling::DynamicLibraryManager::getSymbolLocation(triggerFunc); + assert(!llvm::sys::fs::is_symlink_file(dyLibName)); + + if (dyLibName.empty()) { + ::Error("TCling::UnRegisterModule", "Dictionary trigger function for %s not found", modulename); + return; + } + + if (gIgnoredPCMNames.find(modulename) == gIgnoredPCMNames.end()) + UnLoadPCM(GetRdictFullPath(dyLibName, modulename)); +} + //////////////////////////////////////////////////////////////////////////////// /// Register classes that already existed prior to their dictionary loading /// and that already had a ClassInfo (and thus would not be refresh via diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index 432c2134b9a64..583b9f2cd30c2 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -254,6 +254,7 @@ class TCling final : public TInterpreter { Bool_t lateRegistration = false, Bool_t hasCxxModule = false); virtual void AddAvailableIndentifiers(TSeqCollection& Idents); + void UnRegisterModule(const char* modulename, void (*triggerFunc)()); void RegisterTClassUpdate(TClass *oldcl,DictFuncPtr_t dict); void UnRegisterTClassUpdate(const TClass *oldcl); @@ -621,9 +622,12 @@ class TCling final : public TInterpreter { void AddFriendToClass(clang::FunctionDecl*, clang::CXXRecordDecl*) const; std::map fPendingRdicts; + std::vector fLoadedRdicts; // ROOT owns the TFile-s. void RegisterRdictForLoadPCM(const std::string &pcmFileNameFullPath, llvm::StringRef *pcmContent); void LoadPCM(std::string pcmFileNameFullPath); - void LoadPCMImpl(TFile &pcmFile); + void LoadPCMImpl(TFile &pcmFile) const; + void UnLoadPCM(std::string pcmFileNameFullPath); + void UnLoadPCMImpl(const TFile &pcmFile); void InitRootmapFile(const char *name); int ReadRootmapFile(const char *rootmapfile, TUniqueString* uniqueString = nullptr);