CMS 3D CMS Logo

PluginManager.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: PluginManager
4 // Class : PluginManager
5 //
6 // Implementation:
7 // <Notes on implementation>
8 //
9 // Original Author: Chris Jones
10 // Created: Wed Apr 4 14:28:58 EDT 2007
11 //
12 
13 // system include files
14 #include <boost/filesystem/operations.hpp>
15 
16 #include <fstream>
17 #include <functional>
18 #include <set>
19 
20 // TEMPORARY
21 #include "TInterpreter.h"
22 #include "TVirtualMutex.h"
23 
24 // user include files
32 
33 namespace edmplugin {
34  //
35  // constants, enums and typedefs
36  //
37 
38  //
39  // static data member definitions
40  //
41 
42  static bool readCacheFile(const boost::filesystem::path& cacheFile,
44  PluginManager::CategoryToInfos& categoryToInfos) {
45  if (exists(cacheFile)) {
46  std::ifstream file(cacheFile.string().c_str());
47  if (not file) {
48  throw cms::Exception("PluginMangerCacheProblem")
49  << "Unable to open the cache file '" << cacheFile.string() << "'. Please check permissions on file";
50  }
51  CacheParser::read(file, dir, categoryToInfos);
52  return true;
53  }
54  return false;
55  }
56  //
57  // constructors and destructor
58  //
59  PluginManager::PluginManager(const PluginManager::Config& iConfig) : searchPath_(iConfig.searchPath()) {
60  using std::placeholders::_1;
62  // This is the filename of a file which contains plugins which exist in the
63  // base release and which should exists in the local area, otherwise they
64  // were removed and we want to catch their usage.
65  const boost::filesystem::path& kPoisonedCacheFile(standard::poisonedCachefileName());
66  //NOTE: This may not be needed :/
68  pfm->newFactory_.connect(std::bind(std::mem_fn(&PluginManager::newFactory), this, _1));
69 
70  // When building a single big executable the plugins are already registered in the
71  // PluginFactoryManager, we therefore only need to populate the categoryToInfos_ map
72  // with the relevant information.
73  for (PluginFactoryManager::const_iterator i = pfm->begin(), e = pfm->end(); i != e; ++i) {
74  categoryToInfos_[(*i)->category()] = (*i)->available();
75  }
76 
77  //read in the files
78  //Since we are looping in the 'precidence' order then the lists in categoryToInfos_ will also be
79  // in that order
80  bool foundAtLeastOneCacheFile = false;
81  std::set<std::string> alreadySeen;
82  for (SearchPath::const_iterator itPath = searchPath_.begin(), itEnd = searchPath_.end(); itPath != itEnd;
83  ++itPath) {
84  //take care of the case where the same path is passed in multiple times
85  if (alreadySeen.find(*itPath) != alreadySeen.end()) {
86  continue;
87  }
88  alreadySeen.insert(*itPath);
90  if (exists(dir)) {
91  if (not is_directory(dir)) {
92  throw cms::Exception("PluginManagerBadPath")
93  << "The path '" << dir.string() << "' for the PluginManager is not a directory";
94  }
95  boost::filesystem::path cacheFile = dir / kCacheFile;
96 
97  if (readCacheFile(cacheFile, dir, categoryToInfos_)) {
98  foundAtLeastOneCacheFile = true;
99  }
100 
101  // We do not check for return code since we do not want to consider a
102  // poison cache file as a valid cache file having been found.
103  boost::filesystem::path poisonedCacheFile = dir / kPoisonedCacheFile;
104  readCacheFile(poisonedCacheFile, dir / "poisoned", categoryToInfos_);
105  }
106  }
107  if (not foundAtLeastOneCacheFile and iConfig.mustHaveCache()) {
108  auto ex = cms::Exception("PluginManagerNoCacheFile")
109  << "No cache files named '" << standard::cachefileName() << "' were found in the directories \n";
110  for (auto const& seen : alreadySeen) {
111  ex << " '" << seen << "'\n";
112  }
113  throw ex;
114  }
115  //Since this should not be called until after 'main' has started, we can set the value
116  loadingLibraryNamed_() = "<loaded by another plugin system>";
117  }
118 
119  // PluginManager::PluginManager(const PluginManager& rhs)
120  // {
121  // // do actual copying here;
122  // }
123 
125 
126  //
127  // assignment operators
128  //
129  // const PluginManager& PluginManager::operator=(const PluginManager& rhs)
130  // {
131  // //An exception safe implementation is
132  // PluginManager temp(rhs);
133  // swap(rhs);
134  //
135  // return *this;
136  // }
137 
138  //
139  // member functions
140  //
142  //
143  // const member functions
144  //
145  namespace {
146  struct PICompare {
147  bool operator()(const PluginInfo& iLHS, const PluginInfo& iRHS) const { return iLHS.name_ < iRHS.name_; }
148  };
149  } // namespace
150 
152  bool throwIfFail = true;
153  return loadableFor_(iCategory, iPlugin, throwIfFail);
154  }
155 
157  const std::string& iPlugin,
158  bool& ioThrowIfFailElseSucceedStatus) {
159  const bool throwIfFail = ioThrowIfFailElseSucceedStatus;
160  ioThrowIfFailElseSucceedStatus = true;
161  CategoryToInfos::iterator itFound = categoryToInfos_.find(iCategory);
162  if (itFound == categoryToInfos_.end()) {
163  if (throwIfFail) {
164  throw cms::Exception("PluginNotFound") << "Unable to find plugin '" << iPlugin << "' because the category '"
165  << iCategory << "' has no known plugins";
166  } else {
167  ioThrowIfFailElseSucceedStatus = false;
168  static const boost::filesystem::path s_path;
169  return s_path;
170  }
171  }
172 
173  PluginInfo i;
174  i.name_ = iPlugin;
175  typedef std::vector<PluginInfo>::iterator PIItr;
176  std::pair<PIItr, PIItr> range = std::equal_range(itFound->second.begin(), itFound->second.end(), i, PICompare());
177 
178  if (range.first == range.second) {
179  if (throwIfFail) {
180  throw cms::Exception("PluginNotFound") << "Unable to find plugin '" << iPlugin << "' in category '" << iCategory
181  << "'. Please check spelling of name.";
182  } else {
183  ioThrowIfFailElseSucceedStatus = false;
184  static const boost::filesystem::path s_path;
185  return s_path;
186  }
187  }
188 
189  if (range.second - range.first > 1) {
190  //see if the come from the same directory
191  if (range.first->loadable_.branch_path() == (range.first + 1)->loadable_.branch_path()) {
192  //std::cout<<range.first->name_ <<" " <<(range.first+1)->name_<<std::endl;
193  throw cms::Exception("MultiplePlugins")
194  << "The plugin '" << iPlugin
195  << "' is found in multiple files \n"
196  " '"
197  << range.first->loadable_.leaf() << "'\n '" << (range.first + 1)->loadable_.leaf()
198  << "'\n"
199  "in directory '"
200  << range.first->loadable_.branch_path().string()
201  << "'.\n"
202  "The code must be changed so the plugin only appears in one plugin file. "
203  "You will need to remove the macro which registers the plugin so it only appears in"
204  " one of these files.\n"
205  " If none of these files register such a plugin, "
206  "then the problem originates in a library to which all these files link.\n"
207  "The plugin registration must be removed from that library since plugins are not allowed in regular "
208  "libraries.";
209  }
210  }
211 
212  return range.first->loadable_;
213  }
214 
215  namespace {
216  class Sentry {
217  public:
218  Sentry(std::string& iPath, const std::string& iNewPath) : path_(iPath), oldPath_(iPath) { path_ = iNewPath; }
219  ~Sentry() { path_ = oldPath_; }
220 
221  private:
224  };
225  } // namespace
226 
227  const SharedLibrary& PluginManager::load(const std::string& iCategory, const std::string& iPlugin) {
228  askedToLoadCategoryWithPlugin_(iCategory, iPlugin);
229  const boost::filesystem::path& p = loadableFor(iCategory, iPlugin);
230 
231  //have we already loaded this?
232  auto itLoaded = loadables_.find(p);
233  if (itLoaded == loadables_.end()) {
234  //Need to make sure we only have on SharedLibrary loading at a time
235  std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
236  //Another thread may have gotten this while we were waiting on the mutex
237  itLoaded = loadables_.find(p);
238  if (itLoaded == loadables_.end()) {
239  //try to make one
240  goingToLoad_(p);
241  Sentry s(loadingLibraryNamed_(), p.string());
242  //boost::filesystem::path native(p.string());
243  std::shared_ptr<SharedLibrary> ptr;
244  {
245  //TEMPORARY: to avoid possible deadlocks from ROOT, we must
246  // take the lock ourselves
247  R__LOCKGUARD2(gInterpreterMutex);
248  ptr.reset(new SharedLibrary(p));
249  }
250  loadables_[p] = ptr;
251  justLoaded_(*ptr);
252  return *ptr;
253  }
254  }
255  return *(itLoaded->second);
256  }
257 
258  const SharedLibrary* PluginManager::tryToLoad(const std::string& iCategory, const std::string& iPlugin) {
259  askedToLoadCategoryWithPlugin_(iCategory, iPlugin);
260  bool ioThrowIfFailElseSucceedStatus = false;
261  const boost::filesystem::path& p = loadableFor_(iCategory, iPlugin, ioThrowIfFailElseSucceedStatus);
262 
263  if (not ioThrowIfFailElseSucceedStatus) {
264  return nullptr;
265  }
266 
267  //have we already loaded this?
268  auto itLoaded = loadables_.find(p);
269  if (itLoaded == loadables_.end()) {
270  //Need to make sure we only have on SharedLibrary loading at a time
271  std::lock_guard<std::recursive_mutex> guard(pluginLoadMutex());
272  //Another thread may have gotten this while we were waiting on the mutex
273  itLoaded = loadables_.find(p);
274  if (itLoaded == loadables_.end()) {
275  //try to make one
276  goingToLoad_(p);
277  Sentry s(loadingLibraryNamed_(), p.string());
278  //boost::filesystem::path native(p.string());
279  std::shared_ptr<SharedLibrary> ptr;
280  {
281  //TEMPORARY: to avoid possible deadlocks from ROOT, we must
282  // take the lock ourselves
283  R__LOCKGUARD(gInterpreterMutex);
284  ptr.reset(new SharedLibrary(p));
285  }
286  loadables_[p] = ptr;
287  justLoaded_(*ptr);
288  return ptr.get();
289  }
290  }
291  return (itLoaded->second).get();
292  }
293 
294  //
295  // static member functions
296  //
298  PluginManager* manager = singleton();
299  if (nullptr == manager) {
300  throw cms::Exception("PluginManagerNotConfigured")
301  << "PluginManager::get() was called before PluginManager::configure.";
302  }
303  return manager;
304  }
305 
307  PluginManager*& s = singleton();
308  if (nullptr != s) {
309  throw cms::Exception("PluginManagerReconfigured");
310  }
311 
312  const Config& realConfig = iConfig;
313  if (realConfig.searchPath().empty()) {
314  throw cms::Exception("PluginManagerEmptySearchPath");
315  }
316  s = new PluginManager(realConfig);
317  return *s;
318  }
319 
321  static const std::string s_name("static");
322  return s_name;
323  }
324 
326  //NOTE: pluginLoadMutex() indirectly guards this since this value
327  // is only accessible via the Sentry call which us guarded by the mutex
329  return s_name;
330  }
331 
333  CMS_THREAD_SAFE static PluginManager* s_singleton = nullptr;
334  return s_singleton;
335  }
336 
337  bool PluginManager::isAvailable() { return nullptr != singleton(); }
338 
339 } // namespace edmplugin
CategoryToInfos categoryToInfos_
const boost::filesystem::path & loadableFor(const std::string &iCategory, const std::string &iPlugin)
const SharedLibrary * tryToLoad(const std::string &iCategory, const std::string &iPlugin)
const boost::filesystem::path & cachefileName()
Definition: standard.cc:47
static PluginManager & configure(const Config &)
const boost::filesystem::path & poisonedCachefileName()
Definition: standard.cc:52
static PluginManager *& singleton()
std::map< std::string, Infos > CategoryToInfos
Definition: PluginManager.h:54
const boost::filesystem::path & loadableFor_(const std::string &iCategory, const std::string &iPlugin, bool &ioThrowIfFailElseSucceedStatus)
std::string oldPath_
tbb::concurrent_unordered_map< boost::filesystem::path, std::shared_ptr< SharedLibrary >, PluginManagerPathHasher > loadables_
static const std::string & staticallyLinkedLoadingFileName()
if the value returned from loadingFile matches this string then the file is statically linked ...
edm::signalslot::Signal< void(const std::string &, const std::string &)> askedToLoadCategoryWithPlugin_
static bool readCacheFile(const boost::filesystem::path &cacheFile, const boost::filesystem::path &dir, PluginManager::CategoryToInfos &categoryToInfos)
edm::signalslot::Signal< void(const PluginFactoryBase *)> newFactory_
#define CMS_THREAD_SAFE
edm::signalslot::Signal< void(const SharedLibrary &)> justLoaded_
std::string & path_
std::recursive_mutex & pluginLoadMutex()
edm::signalslot::Signal< void(const boost::filesystem::path &)> goingToLoad_
std::string name_
Definition: PluginInfo.h:29
Definition: Config.py:1
Config & searchPath(const SearchPath &iPath)
Definition: PluginManager.h:59
dbl *** dir
Definition: mlp_gen.cc:35
static std::string & loadingLibraryNamed_()
static void read(std::istream &, const boost::filesystem::path &iDirectory, CategoryToInfos &oOut)
Definition: CacheParser.cc:120
std::vector< const PluginFactoryBase * >::const_iterator const_iterator
void newFactory(const PluginFactoryBase *)
const SharedLibrary & load(const std::string &iCategory, const std::string &iPlugin)
static PluginManager * get()
static PluginFactoryManager * get()
PluginManager(const Config &)