Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-25 02:13:52

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