Changeset 4869 for branches/erijo-dev

Show
Ignore:
Timestamp:
03/20/07 05:17:03 (21 months ago)
Author:
erijo
Message:

Add support for unloading unused libraries + misc cleanups.

Location:
branches/erijo-dev/licq/src/plugin
Files:
1 added
2 removed
4 modified
4 moved

Legend:

Unmodified
Added
Removed
  • branches/erijo-dev/licq/src/plugin/CMakeLists.txt

    r4852 r4869  
    11set(licqplugin_SRCS 
    22  pluginloader.cpp 
    3   pluginmanager.cpp 
    4   pluginprototype.cpp 
     3  pluginrepository.cpp 
    54) 
    65 
  • branches/erijo-dev/licq/src/plugin/pluginfactory.h

    r4852 r4869  
    1818 */ 
    1919 
    20 #ifndef LICQ_TPLUGINFACTORYPROXY_H 
    21 #define LICQ_TPLUGINFACTORYPROXY_H 
     20#ifndef LICQ_TPLUGINFACTORY_H 
     21#define LICQ_TPLUGINFACTORY_H 
    2222 
    2323#include "licq/interface/pluginfactory.h" 
    2424#include "utils/dynamiclibrary.h" 
    2525 
    26 #include <boost/noncopyable.hpp> 
    2726#include <boost/shared_ptr.hpp> 
     27#include <cassert> 
    2828 
    2929namespace Licq 
    3030{ 
    3131 
    32 class TPluginFactoryProxy; 
    33 typedef boost::shared_ptr<TPluginFactoryProxy> TPluginFactoryProxyPtr; 
    34  
    35 class TPluginFactoryProxy : private boost::noncopyable 
     32class TPluginFactory : public IPluginFactory 
    3633{ 
    3734private: 
     
    4340 
    4441  /// Function to use when destroying the factory. 
    45   destroyPluginFactory_t FactoryDestructor; 
     42  FnDestroyPluginFactory FactoryDestructor; 
    4643 
    4744public: 
    48   /** \brief Create a new plugin factory proxy. 
     45  /** \brief Create a new plugin factory. 
    4946 
    5047      \param[in] library The library the real factory was loaded from. 
    51       \param[in] factory The real factory. 
    52       \param[in] factoryDestructor The function that should be called to 
    53         destroy the real factory. 
     48      \param[in] constructor The function to call to create the real factory. 
     49      \param[in] destructor The function to call to destroy the real factory. 
    5450  */ 
    55   TPluginFactoryProxy(TDynamicLibrary* library, IPluginFactory* factory, 
    56                       destroyPluginFactory_t factoryDestructor); 
     51  TPluginFactory(TDynamicLibrary* library, 
     52                 FnCreatePluginFactory constructor, 
     53                 FnDestroyPluginFactory destructor); 
    5754 
    5855  /** \brief Destroy the factory. 
     
    6057      Destroys the real factory and unloads the dynamic library. 
    6158  */ 
    62   ~TPluginFactoryProxy(); 
     59  ~TPluginFactory(); 
    6360 
    64   /** \brief Get the real factory. 
    65       \return The real factory. 
    66   */ 
    67   IPluginFactory* getFactory(); 
     61  /// File the factory was loaded from. 
     62  std::string getLibraryFilename() const; 
     63 
     64  // From IPluginFactory 
     65  unsigned int getPluginCount() const; 
     66  TPluginInformation getPluginInformation(unsigned int index) const; 
     67  IPlugin* createPlugin(unsigned int index, TPluginId id, ILog* log); 
    6868}; 
    6969 
    7070} // namespace Licq 
    7171 
    72 inline 
    73 Licq::TPluginFactoryProxy::TPluginFactoryProxy(TDynamicLibrary* library, 
    74                                                IPluginFactory* factory, 
    75                                                destroyPluginFactory_t factoryDestructor) 
    76   : Library(library), Factory(factory), FactoryDestructor(factoryDestructor) 
     72inline Licq::TPluginFactory::TPluginFactory(TDynamicLibrary* library, 
     73                                            FnCreatePluginFactory constructor, 
     74                                            FnDestroyPluginFactory destructor) 
     75  : Library(library), 
     76    Factory(constructor()), 
     77    FactoryDestructor(destructor) 
    7778{ 
    78   // Empty 
     79  assert(Factory != NULL); 
    7980} 
    8081 
    81 inline Licq::TPluginFactoryProxy::~TPluginFactoryProxy() 
     82inline Licq::TPluginFactory::~TPluginFactory() 
    8283{ 
    8384  FactoryDestructor(Factory); 
     
    8586} 
    8687 
    87 inline Licq::IPluginFactory* Licq::TPluginFactoryProxy::getFactory() 
     88inline std::string Licq::TPluginFactory::getLibraryFilename() const 
    8889{ 
    89   return Factory; 
     90  assert(Library != NULL); 
     91  return Library->getFilename(); 
     92} 
     93 
     94inline unsigned int Licq::TPluginFactory::getPluginCount() const 
     95{ 
     96  return Factory->getPluginCount(); 
     97} 
     98 
     99inline Licq::TPluginInformation 
     100Licq::TPluginFactory::getPluginInformation(unsigned int index) const 
     101{ 
     102  return Factory->getPluginInformation(index); 
     103} 
     104 
     105inline Licq::IPlugin* 
     106Licq::TPluginFactory::createPlugin(unsigned int index, TPluginId id, ILog* log) 
     107{ 
     108  return Factory->createPlugin(index, id, log); 
    90109} 
    91110 
  • branches/erijo-dev/licq/src/plugin/pluginloader.cpp

    r4852 r4869  
    1818 */ 
    1919 
    20 #include "licq/interface/pluginfactory.h" 
    2120#include "licq/version.h" 
    2221 
    2322#include "plugin/pluginloader.h" 
    24 #include "plugin/pluginfactoryproxy.h" 
    2523#include "utils/dynamiclibrary.h" 
    2624 
     
    4442} 
    4543 
    46 unsigned int 
    47 Licq::TPluginLoader::loadPluginsFromDir(const bfs::path& dir, TPluginPrototypeList* pluginList) 
     44unsigned int Licq:: 
     45TPluginLoader::loadPluginsFromDir(const bfs::path& dir, 
     46                                  TPluginFactories* factories) 
    4847{ 
    4948  assert(bfs::is_directory(dir) == true); 
     
    5554  { 
    5655    if (!bfs::is_directory(*file)) 
    57       loadCount += loadPluginsFromFile(*file, pluginList); 
     56      loadCount += loadPluginsFromFile(*file, factories); 
    5857  } 
    5958 
     
    6160} 
    6261 
    63 unsigned int 
    64 Licq::TPluginLoader::loadPluginsFromFile(const bfs::path& file, TPluginPrototypeList* pluginList) 
     62unsigned int Licq:: 
     63TPluginLoader::loadPluginsFromFile(const bfs::path& file, 
     64                                   TPluginFactories* factories) 
    6565{ 
    6666  assert(bfs::is_directory(file) == false); 
     
    8080 
    8181  // First check that the plugin and daemon uses the same API version 
    82   getPluginApiVersion_t apiVersion; 
     82  FnGetPluginApiVersion apiVersion; 
    8383  if (!lib->loadSymbol("licqGetPluginApiVersion", &apiVersion)) 
    8484    return 0; 
     
    9393 
    9494  // Load symbols for creating and destroying plugin factory 
    95   createPluginFactory_t createPluginFactory; 
     95  FnCreatePluginFactory createPluginFactory; 
    9696  if (!lib->loadSymbol("licqCreatePluginFactory", &createPluginFactory)) 
    9797    return 0; 
    9898 
    99   destroyPluginFactory_t destroyPluginFactory; 
     99  FnDestroyPluginFactory destroyPluginFactory; 
    100100  if (!lib->loadSymbol("licqDestroyPluginFactory", &destroyPluginFactory)) 
    101101    return 0; 
    102102 
    103   // Try and create the plugin factory 
    104   TPluginFactoryProxyPtr 
    105     factoryProxy(new TPluginFactoryProxy(libptr.release(), 
    106                                          createPluginFactory(), 
    107                                          destroyPluginFactory)); 
    108   if (factoryProxy->getFactory() == NULL) 
     103  // Create the plugin factory 
     104  boost::shared_ptr<TPluginFactory> 
     105    factory(new TPluginFactory(libptr.release(), 
     106                               createPluginFactory, 
     107                               destroyPluginFactory)); 
     108 
     109  if (factory->getPluginCount() == 0) 
    109110  { 
    110     Log->error("Failed to create plugin factory for %s", filename.c_str()); 
     111    // No point in saving an empty factory. 
     112    Log->debug("IPluginFactory::getPluginCount() == 0 for %s", filename.c_str()); 
    111113    return 0; 
    112114  } 
    113115 
    114   // Create the "plugins" 
    115   const unsigned int count = factoryProxy->getFactory()->getPluginCount(); 
    116   for (unsigned int i = 0; i < count; ++i) 
    117     pluginList->push_back(new TPluginPrototype(factoryProxy, i)); 
    118  
    119   // If count = 0, the factory will be destroyed and the library unloaded 
    120   return count; 
     116  // Save it 
     117  factories->push_back(factory); 
     118  return 1; 
    121119} 
    122120 
     
    127125} 
    128126 
    129 unsigned int Licq::TPluginLoader::loadPlugins(const std::string& path, TPluginPrototypeList* pluginList) 
     127unsigned int Licq:: 
     128TPluginLoader::loadPlugins(const std::string& path, 
     129                           TPluginFactories* factories) 
    130130{ 
    131   assert(pluginList != NULL); 
     131  assert(factories != NULL); 
    132132 
    133133  if (path.empty()) 
     
    144144 
    145145    if (bfs::is_directory(absolute)) 
    146       return loadPluginsFromDir(absolute, pluginList); 
     146      return loadPluginsFromDir(absolute, factories); 
    147147    else 
    148       return loadPluginsFromFile(absolute, pluginList); 
     148      return loadPluginsFromFile(absolute, factories); 
    149149  } 
    150   catch (const bfs::filesystem_error& e) 
     150  catch (const bfs::filesystem_error& ex) 
    151151  { 
    152     Log->error("Failed to load plugins from \"%s\": %s", path.c_str(), e.what()); 
     152    Log->error("Failed to load plugins from \"%s\": %s", path.c_str(), ex.what()); 
    153153  } 
    154154 
  • branches/erijo-dev/licq/src/plugin/pluginloader.h

    r4852 r4869  
    2222 
    2323#include "licq/interface/log.h" 
    24 #include "plugin/pluginprototype.h" 
     24#include "plugin/pluginfactory.h" 
    2525 
     26#include <boost/shared_ptr.hpp> 
     27#include <list> 
     28 
     29// Forward declaration 
    2630namespace boost 
    2731{ 
    2832namespace filesystem 
    2933{ 
    30   class path; 
     34class path; 
    3135} 
    3236} 
     
    3438namespace Licq 
    3539{ 
     40 
     41typedef std::list< boost::shared_ptr<TPluginFactory> > TPluginFactories; 
    3642 
    3743class TPluginLoader 
     
    4147 
    4248  /** \brief Load plugins from all files in given directory. 
    43       \return The number of plugins loaded. 
     49      \return The number of plugin factories loaded. 
    4450  */ 
    45   unsigned int loadPluginsFromDir(const boost::filesystem::path& dir, TPluginPrototypeList* pluginList); 
     51  unsigned int loadPluginsFromDir(const boost::filesystem::path& dir, 
     52                                  TPluginFactories* factories); 
    4653 
    4754  /** \brief Load all plugins in given file. 
    48       \return The number of plugins loaded. 
     55      \return The number of plugin factories loaded. Since a single file 
     56      can only contain zero or one factory, this method always returns 0 or 1. 
    4957  */ 
    50   unsigned int loadPluginsFromFile(const boost::filesystem::path& file, TPluginPrototypeList* pluginList); 
     58  unsigned int loadPluginsFromFile(const boost::filesystem::path& file, 
     59                                   TPluginFactories* factories); 
    5160 
    5261public: 
    5362  /// Creates a new plugin loader. 
    54   TPluginLoader(ILog* log); 
     63  explicit TPluginLoader(ILog* log); 
    5564 
    5665  /** \brief Load all plugins from the given path. 
    5766 
    5867      \param[in] path Path to seach for plugins. 
    59       \param[out] pluginList Loaded plugins will be added to the back of this list. 
    60         The ownership of all returned plugins is transfered to the caller. 
     68      \param[out] factories Loaded factories will be added to the back. 
    6169 
    62       \return The number of plugins loaded. 
     70      \return The number of plugin factories loaded. 
    6371  */ 
    64   unsigned int loadPlugins(const std::string& path, TPluginPrototypeList* pluginList); 
     72  unsigned int loadPlugins(const std::string& path, 
     73                           TPluginFactories* factories); 
    6574}; 
    6675 
  • branches/erijo-dev/licq/src/plugin/pluginrepository.cpp

    r4852 r4869  
    1818 */ 
    1919 
    20 #include "plugin/pluginmanager.h" 
     20#include "plugin/plugin.h" 
     21#include "plugin/pluginrepository.h" 
    2122#include "utils/misc.h" 
    2223 
    23 Licq::TPluginManager::TPluginManager(ILog* log) 
     24Licq::TPluginRepository::TPluginRepository(ILog* log) 
    2425  : Log(log), PluginLoader(log), NextPluginId(1) 
    2526{ 
     
    2728} 
    2829 
    29 Licq::TPluginManager::~TPluginManager() 
     30Licq::TPluginRepository::~TPluginRepository() 
    3031{ 
    31   std::for_each(PluginPrototypeList.begin(), PluginPrototypeList.end(), ObjectDeleter()); 
     32  std::for_each(PluginCache.begin(), PluginCache.end(), ObjectDeleter()); 
    3233} 
    3334 
    34 void Licq::TPluginManager::addPluginSearchPath(const std::string& path) 
     35void Licq::TPluginRepository::addSearchPath(const std::string& path) 
    3536{ 
    36   unsigned int count = PluginLoader.loadPlugins(path, &PluginPrototypeList); 
    37   Log->debug("Loaded %d plugin%s from '%s'", count, (count == 1 ? "" : "s"), path.c_str()); 
     37  PluginPaths.push_back(path); 
     38} 
    3839 
    39   // TODO Remove duplicate plugins (same name, remove older IntVersion) 
     40void Licq::TPluginRepository::rebuildCache() 
     41{ 
     42  // Remove all old factories 
     43  PluginFactories.clear(); 
    4044 
    41   TPluginPrototypeList::iterator it = PluginPrototypeList.begin(); 
    42   for (; it != PluginPrototypeList.end(); ++it) 
     45  for (TStringList::const_iterator path = PluginPaths.begin(); 
     46       path != PluginPaths.end(); ++path) 
    4347  { 
    44     Log->info("Plugin %s version %s.", 
    45               (*it)->getPluginInformation().Name.c_str(), 
    46               (*it)->getPluginInformation().Version.c_str()); 
    47     Log->info("       %s", (*it)->getPluginInformation().Description.c_str()); 
     48    const unsigned int count = PluginLoader.loadPlugins(*path, &PluginFactories); 
     49    Log->debug("Loaded %u plugin%s from %s", 
     50               count, (count == 1 ? "" : "s"), path->c_str()); 
     51  } 
     52 
     53  // Clear the cache 
     54  std::for_each(PluginCache.begin(), PluginCache.end(), ObjectDeleter()); 
     55 
     56  for (TPluginFactories::const_iterator factory = PluginFactories.begin(); 
     57       factory != PluginFactories.end(); ++factory) 
     58  { 
     59    const unsigned int count = (*factory)->getPluginCount(); 
     60    for (unsigned int index = 0; index < count; ++index) 
     61    { 
     62      TCacheEntry* entry = new TCacheEntry; 
     63      entry->LibraryFilename = (*factory)->getLibraryFilename(); 
     64      entry->Factory = *factory; 
     65      entry->Index = index; 
     66      entry->Information = (*factory)->getPluginInformation(index); 
     67 
     68      // Check for duplicate plugins 
     69      TPluginCache::iterator cache = PluginCache.find(entry->Information.Name); 
     70      if (cache != PluginCache.end()) 
     71      { 
     72        // Keep the one with the highest IntVersion, or if equal, the first loaded. 
     73        if (entry->Information.IntVersion <= cache->second->Information.IntVersion) 
     74        { 
     75          delete entry; 
     76          continue; 
     77        } 
     78        else 
     79        { 
     80          delete cache->second; 
     81          PluginCache.erase(cache); 
     82        } 
     83      } 
     84 
     85      PluginCache[entry->Information.Name] = entry; 
     86    } 
    4887  } 
    4988} 
     89 
     90void Licq::TPluginRepository::unloadUnusedPlugins() 
     91{ 
     92  PluginFactories.clear(); 
     93} 
     94 
     95void Licq::TPluginRepository:: 
     96getAvailablePlugins(std::list<TPluginInformation>* pluginInformation) 
     97{ 
     98  for (TPluginCache::const_iterator cache = PluginCache.begin(); 
     99       cache != PluginCache.end(); ++cache) 
     100  { 
     101    pluginInformation->push_back(cache->second->Information); 
     102  } 
     103} 
     104 
     105Licq::IPlugin* Licq::TPluginRepository::createPlugin(const std::string& name) 
     106{ 
     107  TPluginCache::const_iterator cache = PluginCache.find(name); 
     108  if (cache == PluginCache.end()) 
     109  { 
     110    Log->error("No such plugin %s", name.c_str()); 
     111    return NULL; 
     112  } 
     113 
     114  boost::shared_ptr<IPluginFactory> factory = cache->second->Factory.lock(); 
     115  if (!factory) 
     116  { 
     117    // Need to reload the library 
     118    TPluginFactories factories; 
     119    if (PluginLoader.loadPlugins(cache->second->LibraryFilename, &factories) == 0) 
     120    { 
     121      Log->error("Failed to reopen plugin library %s", 
     122                 cache->second->LibraryFilename.c_str()); 
     123      return NULL; 
     124    } 
     125 
     126    assert(factories.size() == 1); 
     127    factory = factories.front(); 
     128  } 
     129   
     130  assert(factory); 
     131  IPlugin* plugin = factory->createPlugin(cache->second->Index, NextPluginId, Log); 
     132 
     133  if (plugin != NULL) 
     134  { 
     135    NextPluginId += 1; 
     136    return new TPlugin(plugin, factory); 
     137  } 
     138 
     139  return NULL; 
     140} 
  • branches/erijo-dev/licq/src/plugin/pluginrepository.h

    r4852 r4869  
    1818 */ 
    1919 
    20 #ifndef LICQ_TPLUGINMANAGER_H 
    21 #define LICQ_TPLUGINMANAGER_H 
     20#ifndef LICQ_TPLUGINREPOSITORY_H 
     21#define LICQ_TPLUGINREPOSITORY_H 
    2222 
    2323#include "licq/interface/log.h" 
     24#include "licq/interface/plugin.h" 
     25#include "licq/interface/plugininformation.h" 
     26 
     27#include "plugin/pluginfactory.h" 
    2428#include "plugin/pluginloader.h" 
    25 #include "plugin/pluginprototype.h" 
    2629 
    27 #include <boost/noncopyable.hpp> 
     30#include <boost/weak_ptr.hpp> 
     31#include <map> 
     32#include <string> 
    2833 
    2934namespace Licq 
    3035{ 
    3136 
    32 class TPluginManager : boost::noncopyable 
     37class TPluginRepository 
    3338{ 
    3439private: 
    3540  ILog* Log; 
    3641 
     42  /// A list of paths to search for plugins. 
     43  TStringList PluginPaths; 
     44 
    3745  TPluginLoader PluginLoader; 
    38   TPluginPrototypeList PluginPrototypeList; 
     46 
     47  /// By keeping factories in this list, their dynamic library is keept in memory. 
     48  TPluginFactories PluginFactories; 
     49 
     50  struct TCacheEntry 
     51  { 
     52    std::string LibraryFilename; 
     53    boost::weak_ptr<IPluginFactory> Factory; 
     54    unsigned int Index; 
     55    TPluginInformation Information; 
     56  }; 
     57 
     58  typedef std::map<std::string, TCacheEntry*> TPluginCache; 
     59  TPluginCache PluginCache; 
    3960 
    4061  TPluginId NextPluginId; 
    4162 
    4263public: 
    43   TPluginManager(ILog* log); 
    44   ~TPluginManager(); 
     64  explicit TPluginRepository(ILog* log); 
     65  ~TPluginRepository(); 
    4566 
    46   void addPluginSearchPath(const std::string& path); 
     67  void addSearchPath(const std::string& path); 
     68  void rebuildCache(); 
     69 
     70  void unloadUnusedPlugins(); 
     71 
     72  void getAvailablePlugins(std::list<TPluginInformation>* pluginInformation); 
     73 
     74  IPlugin* createPlugin(const std::string& name); 
    4775}; 
    4876 
  • branches/erijo-dev/licq/src/plugin/tests/CMakeLists.txt

    r4852 r4869  
    11set(licqplugintest_SRCS 
    22  main.cpp 
    3   pluginfactoryproxytest.cpp 
     3  pluginfactorytest.cpp 
    44) 
    55 
  • branches/erijo-dev/licq/src/plugin/tests/pluginfactorytest.cpp

    r4852 r4869  
    2121 
    2222#include "licq/interface/pluginfactory.h" 
    23 #include "plugin/pluginfactoryproxy.h" 
     23#include "plugin/pluginfactory.h" 
     24 
     25class TPluginFactory : public Licq::IPluginFactory 
     26{ 
     27public: 
     28  virtual unsigned int getPluginCount() const { return 0; } 
     29  virtual Licq::TPluginInformation getPluginInformation(unsigned int) const 
     30  { 
     31    return Licq::TPluginInformation(); 
     32  } 
     33 
     34  virtual Licq::IPlugin* createPlugin(unsigned int, Licq::TPluginId, Licq::ILog*) 
     35  { 
     36    return NULL; 
     37  } 
     38}; 
    2439 
    2540static unsigned int DestructorCount = 0; 
     41static TPluginFactory PluginFactory; 
     42static Licq::IPluginFactory* DestroyedPluginFactory = NULL; 
    2643 
    27 void licqDestroyPluginFactory(Licq::IPluginFactory*) 
     44Licq::IPluginFactory* licqCreatePluginFactory() 
    2845{ 
     46  return &PluginFactory; 
     47} 
     48 
     49void licqDestroyPluginFactory(Licq::IPluginFactory* factory) 
     50{ 
     51  DestroyedPluginFactory = factory; 
    2952  DestructorCount += 1; 
    3053} 
    3154 
    32 BOOST_AUTO_UNIT_TEST( tc_pluginfactoryproxy ) 
     55BOOST_AUTO_UNIT_TEST( tc_pluginfactory ) 
    3356{ 
     57  BOOST_CHECK_EQUAL(DestroyedPluginFactory, (Licq::IPluginFactory*)NULL); 
    3458  BOOST_CHECK_EQUAL(DestructorCount, 0u); 
    3559  { 
    36     Licq::TPluginFactoryProxy proxy(NULL, NULL, &licqDestroyPluginFactory); 
    37     BOOST_CHECK_EQUAL(proxy.getFactory(), (Licq::IPluginFactory*)NULL); 
     60    Licq::TPluginFactory factory(NULL,  
     61                                 &licqCreatePluginFactory,  
     62                                 &licqDestroyPluginFactory); 
    3863  } 
    3964  BOOST_CHECK_EQUAL(DestructorCount, 1u); 
     65  BOOST_CHECK_EQUAL(&PluginFactory, DestroyedPluginFactory); 
    4066}