File indexing completed on 2024-04-25 02:13:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <filesystem>
0015 #include <fstream>
0016 #include <functional>
0017 #include <set>
0018
0019
0020 #include "TInterpreter.h"
0021 #include "TVirtualMutex.h"
0022
0023
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
0035
0036
0037
0038
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
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
0062
0063
0064 const std::filesystem::path& kPoisonedCacheFile(standard::poisonedCachefileName());
0065
0066 PluginFactoryManager* pfm = PluginFactoryManager::get();
0067 pfm->newFactory_.connect(std::bind(std::mem_fn(&PluginManager::newFactory), this, _1));
0068
0069
0070
0071
0072 for (PluginFactoryManager::const_iterator i = pfm->begin(), e = pfm->end(); i != e; ++i) {
0073 categoryToInfos_[(*i)->category()] = (*i)->available();
0074 }
0075
0076
0077
0078
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
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
0101
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
0118 loadingLibraryNamed_() = "<loaded by another plugin system>";
0119 }
0120
0121
0122
0123
0124
0125
0126 PluginManager::~PluginManager() {}
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143 void PluginManager::newFactory(const PluginFactoryBase*) {}
0144
0145
0146
0147 namespace {
0148 struct PICompare {
0149 bool operator()(const PluginInfo& iLHS, const PluginInfo& iRHS) const { return iLHS.name_ < iRHS.name_; }
0150 };
0151 }
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
0193 if (range.first->loadable_.parent_path() == (range.first + 1)->loadable_.parent_path()) {
0194
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 }
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
0234 auto itLoaded = loadables_.find(p);
0235 if (itLoaded == loadables_.end()) {
0236
0237 std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
0238
0239 itLoaded = loadables_.find(p);
0240 if (itLoaded == loadables_.end()) {
0241
0242 goingToLoad_(p);
0243 Sentry s(loadingLibraryNamed_(), p.string());
0244
0245 std::shared_ptr<SharedLibrary> ptr;
0246 {
0247
0248
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
0275 auto itLoaded = loadables_.find(p);
0276 if (itLoaded == loadables_.end()) {
0277
0278 std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
0279
0280 itLoaded = loadables_.find(p);
0281 if (itLoaded == loadables_.end()) {
0282
0283 goingToLoad_(p);
0284 Sentry s(loadingLibraryNamed_(), p.string());
0285
0286 std::shared_ptr<SharedLibrary> ptr;
0287 {
0288
0289
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
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
0339
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 }