Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-11-09 02:40:43

0001 // -*- C++ -*-
0002 //
0003 // Package:     PluginManager
0004 // Class  :     PluginManager
0005 //
0006 // Implementation:
0007 //     <Notes on implementation>
0008 //
0009 // Original Author:  Chris Jones
0010 //         Created:  Wed Apr  4 14:28:58 EDT 2007
0011 //
0012 
0013 // system include files
0014 #include <filesystem>
0015 #include <fstream>
0016 #include <functional>
0017 #include <set>
0018 
0019 // TEMPORARY
0020 #include "TInterpreter.h"
0021 #include "TVirtualMutex.h"
0022 
0023 // user include files
0024 #include "FWCore/PluginManager/interface/CacheParser.h"
0025 #include "FWCore/PluginManager/interface/PluginFactoryBase.h"
0026 #include "FWCore/PluginManager/interface/PluginFactoryManager.h"
0027 #include "FWCore/PluginManager/interface/PluginManager.h"
0028 #include "FWCore/PluginManager/interface/standard.h"
0029 #include "FWCore/Utilities/interface/Exception.h"
0030 #include "FWCore/Utilities/interface/thread_safety_macros.h"
0031 
0032 #include "PauseMaxMemoryPreloadSentry.h"
0033 
0034 namespace edmplugin {
0035   //
0036   // constants, enums and typedefs
0037   //
0038 
0039   //
0040   // static data member definitions
0041   //
0042 
0043   static bool readCacheFile(const std::filesystem::path& cacheFile,
0044                             const std::filesystem::path& dir,
0045                             PluginManager::CategoryToInfos& categoryToInfos) {
0046     if (exists(cacheFile)) {
0047       std::ifstream file(cacheFile.string().c_str());
0048       if (not file) {
0049         throw cms::Exception("PluginMangerCacheProblem")
0050             << "Unable to open the cache file '" << cacheFile.string() << "'. Please check permissions on file";
0051       }
0052       edm::PauseMaxMemoryPreloadSentry pauseSentry;
0053       CacheParser::read(file, dir, categoryToInfos);
0054       return true;
0055     }
0056     return false;
0057   }
0058   //
0059   // constructors and destructor
0060   //
0061   PluginManager::PluginManager(const PluginManager::Config& iConfig) : searchPath_(iConfig.searchPath()) {
0062     using std::placeholders::_1;
0063     const std::filesystem::path& kCacheFile(standard::cachefileName());
0064     // This is the filename of a file which contains plugins which exist in the
0065     // base release and which should exists in the local area, otherwise they
0066     // were removed and we want to catch their usage.
0067     const std::filesystem::path& kPoisonedCacheFile(standard::poisonedCachefileName());
0068     //NOTE: This may not be needed :/
0069     PluginFactoryManager* pfm = PluginFactoryManager::get();
0070     pfm->newFactory_.connect(std::bind(std::mem_fn(&PluginManager::newFactory), this, _1));
0071 
0072     // When building a single big executable the plugins are already registered in the
0073     // PluginFactoryManager, we therefore only need to populate the categoryToInfos_ map
0074     // with the relevant information.
0075     for (PluginFactoryManager::const_iterator i = pfm->begin(), e = pfm->end(); i != e; ++i) {
0076       categoryToInfos_[(*i)->category()] = (*i)->available();
0077     }
0078 
0079     //read in the files
0080     //Since we are looping in the 'precidence' order then the lists in categoryToInfos_ will also be
0081     // in that order
0082     bool foundAtLeastOneCacheFile = false;
0083     std::set<std::string> alreadySeen;
0084     for (SearchPath::const_iterator itPath = searchPath_.begin(), itEnd = searchPath_.end(); itPath != itEnd;
0085          ++itPath) {
0086       //take care of the case where the same path is passed in multiple times
0087       if (alreadySeen.find(*itPath) != alreadySeen.end()) {
0088         continue;
0089       }
0090       alreadySeen.insert(*itPath);
0091       std::filesystem::path dir(*itPath);
0092       if (exists(dir)) {
0093         if (not is_directory(dir)) {
0094           throw cms::Exception("PluginManagerBadPath")
0095               << "The path '" << dir.string() << "' for the PluginManager is not a directory";
0096         }
0097         std::filesystem::path cacheFile = dir / kCacheFile;
0098 
0099         if (readCacheFile(cacheFile, dir, categoryToInfos_)) {
0100           foundAtLeastOneCacheFile = true;
0101         }
0102 
0103         // We do not check for return code since we do not want to consider a
0104         // poison cache file as a valid cache file having been found.
0105         std::filesystem::path poisonedCacheFile = dir / kPoisonedCacheFile;
0106         readCacheFile(poisonedCacheFile, dir / "poisoned", categoryToInfos_);
0107       }
0108     }
0109     if (not foundAtLeastOneCacheFile and iConfig.mustHaveCache()) {
0110       auto ex = cms::Exception("PluginManagerNoCacheFile")
0111                 << "No cache files named '" << standard::cachefileName() << "' were found in the directories \n";
0112       for (auto const& seen : alreadySeen) {
0113         ex << " '" << seen << "'\n";
0114       }
0115       throw ex;
0116     }
0117     if (iConfig.mustHaveCache() and categoryToInfos_.empty()) {
0118       throw cms::Exception("PluginManagerCacheFilesEmpty") << "Cache files were found but all were empty.";
0119     }
0120     //Since this should not be called until after 'main' has started, we can set the value
0121     loadingLibraryNamed_() = "<loaded by another plugin system>";
0122   }
0123 
0124   // PluginManager::PluginManager(const PluginManager& rhs)
0125   // {
0126   //    // do actual copying here;
0127   // }
0128 
0129   PluginManager::~PluginManager() {}
0130 
0131   //
0132   // assignment operators
0133   //
0134   // const PluginManager& PluginManager::operator=(const PluginManager& rhs)
0135   // {
0136   //   //An exception safe implementation is
0137   //   PluginManager temp(rhs);
0138   //   swap(rhs);
0139   //
0140   //   return *this;
0141   // }
0142 
0143   //
0144   // member functions
0145   //
0146   void PluginManager::newFactory(const PluginFactoryBase*) {}
0147   //
0148   // const member functions
0149   //
0150   namespace {
0151     struct PICompare {
0152       bool operator()(const PluginInfo& iLHS, const PluginInfo& iRHS) const { return iLHS.name_ < iRHS.name_; }
0153     };
0154   }  // namespace
0155 
0156   const std::filesystem::path& PluginManager::loadableFor(const std::string& iCategory, const std::string& iPlugin) {
0157     bool throwIfFail = true;
0158     return loadableFor_(iCategory, iPlugin, throwIfFail);
0159   }
0160 
0161   const std::filesystem::path& PluginManager::loadableFor_(const std::string& iCategory,
0162                                                            const std::string& iPlugin,
0163                                                            bool& ioThrowIfFailElseSucceedStatus) {
0164     const bool throwIfFail = ioThrowIfFailElseSucceedStatus;
0165     ioThrowIfFailElseSucceedStatus = true;
0166     CategoryToInfos::iterator itFound = categoryToInfos_.find(iCategory);
0167     if (itFound == categoryToInfos_.end()) {
0168       if (throwIfFail) {
0169         throw cms::Exception("PluginNotFound") << "Unable to find plugin '" << iPlugin << "' because the category '"
0170                                                << iCategory << "' has no known plugins";
0171       } else {
0172         ioThrowIfFailElseSucceedStatus = false;
0173         static const std::filesystem::path s_path;
0174         return s_path;
0175       }
0176     }
0177 
0178     PluginInfo i;
0179     i.name_ = iPlugin;
0180     typedef std::vector<PluginInfo>::iterator PIItr;
0181     std::pair<PIItr, PIItr> range = std::equal_range(itFound->second.begin(), itFound->second.end(), i, PICompare());
0182 
0183     if (range.first == range.second) {
0184       if (throwIfFail) {
0185         throw cms::Exception("PluginNotFound") << "Unable to find plugin '" << iPlugin << "' in category '" << iCategory
0186                                                << "'. Please check spelling of name.";
0187       } else {
0188         ioThrowIfFailElseSucceedStatus = false;
0189         static const std::filesystem::path s_path;
0190         return s_path;
0191       }
0192     }
0193 
0194     if (range.second - range.first > 1) {
0195       //see if the come from the same directory
0196       if (range.first->loadable_.parent_path() == (range.first + 1)->loadable_.parent_path()) {
0197         //std::cout<<range.first->name_ <<" " <<(range.first+1)->name_<<std::endl;
0198         throw cms::Exception("MultiplePlugins")
0199             << "The plugin '" << iPlugin
0200             << "' is found in multiple files \n"
0201                " '"
0202             << range.first->loadable_.filename() << "'\n '" << (range.first + 1)->loadable_.filename()
0203             << "'\n"
0204                "in directory '"
0205             << range.first->loadable_.parent_path().string()
0206             << "'.\n"
0207                "The code must be changed so the plugin only appears in one plugin file. "
0208                "You will need to remove the macro which registers the plugin so it only appears in"
0209                " one of these files.\n"
0210                "  If none of these files register such a plugin, "
0211                "then the problem originates in a library to which all these files link.\n"
0212                "The plugin registration must be removed from that library since plugins are not allowed in regular "
0213                "libraries.";
0214       }
0215     }
0216 
0217     return range.first->loadable_;
0218   }
0219 
0220   namespace {
0221     class Sentry {
0222     public:
0223       Sentry(std::string& iPath, const std::string& iNewPath) : path_(iPath), oldPath_(iPath) { path_ = iNewPath; }
0224       ~Sentry() { path_ = oldPath_; }
0225 
0226     private:
0227       std::string& path_;
0228       std::string oldPath_;
0229     };
0230   }  // namespace
0231 
0232   const SharedLibrary& PluginManager::load(const std::string& iCategory, const std::string& iPlugin) {
0233     askedToLoadCategoryWithPlugin_(iCategory, iPlugin);
0234     const std::filesystem::path& p = loadableFor(iCategory, iPlugin);
0235 
0236     //have we already loaded this?
0237     auto itLoaded = loadables_.find(p);
0238     if (itLoaded == loadables_.end()) {
0239       //Need to make sure we only have on SharedLibrary loading at a time
0240       std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
0241       //Another thread may have gotten this while we were waiting on the mutex
0242       itLoaded = loadables_.find(p);
0243       if (itLoaded == loadables_.end()) {
0244         //try to make one
0245         goingToLoad_(p);
0246         Sentry s(loadingLibraryNamed_(), p.string());
0247         //std::filesystem::path native(p.string());
0248         std::shared_ptr<SharedLibrary> ptr;
0249         {
0250           //TEMPORARY: to avoid possible deadlocks from ROOT, we must
0251           // take the lock ourselves
0252           R__LOCKGUARD2(gInterpreterMutex);
0253           try {
0254             ptr = std::make_shared<SharedLibrary>(p);
0255           } catch (cms::Exception& e) {
0256             e.addContext("While attempting to load plugin " + iPlugin);
0257             throw;
0258           }
0259         }
0260         loadables_.emplace(p, ptr);
0261         justLoaded_(*ptr);
0262         return *ptr;
0263       }
0264     }
0265     return *(itLoaded->second);
0266   }
0267 
0268   const SharedLibrary* PluginManager::tryToLoad(const std::string& iCategory, const std::string& iPlugin) {
0269     askedToLoadCategoryWithPlugin_(iCategory, iPlugin);
0270     bool ioThrowIfFailElseSucceedStatus = false;
0271     const std::filesystem::path& p = loadableFor_(iCategory, iPlugin, ioThrowIfFailElseSucceedStatus);
0272 
0273     if (not ioThrowIfFailElseSucceedStatus) {
0274       return nullptr;
0275     }
0276 
0277     //have we already loaded this?
0278     auto itLoaded = loadables_.find(p);
0279     if (itLoaded == loadables_.end()) {
0280       //Need to make sure we only have on SharedLibrary loading at a time
0281       std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
0282       //Another thread may have gotten this while we were waiting on the mutex
0283       itLoaded = loadables_.find(p);
0284       if (itLoaded == loadables_.end()) {
0285         //try to make one
0286         goingToLoad_(p);
0287         Sentry s(loadingLibraryNamed_(), p.string());
0288         //std::filesystem::path native(p.string());
0289         std::shared_ptr<SharedLibrary> ptr;
0290         {
0291           //TEMPORARY: to avoid possible deadlocks from ROOT, we must
0292           // take the lock ourselves
0293           R__LOCKGUARD(gInterpreterMutex);
0294           try {
0295             ptr = std::make_shared<SharedLibrary>(p);
0296           } catch (cms::Exception& e) {
0297             e.addContext("While attempting to load plugin " + iPlugin);
0298             throw;
0299           }
0300         }
0301         loadables_[p] = ptr;
0302         justLoaded_(*ptr);
0303         return ptr.get();
0304       }
0305     }
0306     return (itLoaded->second).get();
0307   }
0308 
0309   //
0310   // static member functions
0311   //
0312   PluginManager* PluginManager::get() {
0313     PluginManager* manager = singleton();
0314     if (nullptr == manager) {
0315       throw cms::Exception("PluginManagerNotConfigured")
0316           << "PluginManager::get() was called before PluginManager::configure.";
0317     }
0318     return manager;
0319   }
0320 
0321   PluginManager& PluginManager::configure(const Config& iConfig) {
0322     PluginManager*& s = singleton();
0323     if (nullptr != s) {
0324       throw cms::Exception("PluginManagerReconfigured");
0325     }
0326 
0327     const Config& realConfig = iConfig;
0328     if (realConfig.searchPath().empty()) {
0329       throw cms::Exception("PluginManagerEmptySearchPath");
0330     }
0331     s = new PluginManager(realConfig);
0332     return *s;
0333   }
0334 
0335   const std::string& PluginManager::staticallyLinkedLoadingFileName() {
0336     static const std::string s_name("static");
0337     return s_name;
0338   }
0339 
0340   std::string& PluginManager::loadingLibraryNamed_() {
0341     //NOTE: pluginLoadMutex() indirectly guards this since this value
0342     // is only accessible via the Sentry call which us guarded by the mutex
0343     CMS_THREAD_SAFE static std::string s_name(staticallyLinkedLoadingFileName());
0344     return s_name;
0345   }
0346 
0347   PluginManager*& PluginManager::singleton() {
0348     CMS_THREAD_SAFE static PluginManager* s_singleton = nullptr;
0349     return s_singleton;
0350   }
0351 
0352   bool PluginManager::isAvailable() { return nullptr != singleton(); }
0353 
0354 }  // namespace edmplugin