Program Listing for File class_loader_core.hpp
↰ Return to documentation for file (include/class_loader/class_loader_core.hpp
)
/*
* Software License Agreement (BSD License)
*
* Copyright (c) 2012, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLASS_LOADER__CLASS_LOADER_CORE_HPP_
#define CLASS_LOADER__CLASS_LOADER_CORE_HPP_
#include <cstddef>
#include <cstdio>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <typeinfo>
#include <utility>
#include <vector>
// TODO(mikaelarguedas) remove this once console_bridge complies with this
// see https://github.com/ros/console_bridge/issues/55
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#include "console_bridge/console.h"
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#include "class_loader/exceptions.hpp"
#include "class_loader/meta_object.hpp"
#include "class_loader/visibility_control.hpp"
#include "rcpputils/shared_library.hpp"
namespace class_loader
{
class ClassLoader; // Forward declaration
namespace impl
{
// Typedefs
typedef std::string LibraryPath;
typedef std::string ClassName;
typedef std::string BaseClassName;
typedef std::map<ClassName, impl::AbstractMetaObjectBase *> FactoryMap;
typedef std::map<BaseClassName, FactoryMap> BaseToFactoryMapMap;
typedef std::pair<LibraryPath, std::shared_ptr<rcpputils::SharedLibrary>> LibraryPair;
typedef std::vector<LibraryPair> LibraryVector;
typedef std::vector<AbstractMetaObjectBase *> MetaObjectVector;
class MetaObjectGraveyardVector : public MetaObjectVector
{
public:
~MetaObjectGraveyardVector()
{
// Make sure not to access the pointer value in the static variable of `getMetaObjectGraveyard()
// when destroying `meta_object` in the unique_ptr deleter. Because the static variable in
// `getMetaObjectGraveyard()` can be destructed before the static variable
// `g_register_plugin_ ## UniqueID` in some circumstances.
// NOTE of the vector dtor in the STL: if the elements themselves are pointers, the pointed-to
// memory is not touched in any way. Managing the pointer is the user's responsibility.
clear();
}
};
CLASS_LOADER_PUBLIC
void printDebugInfoToScreen();
// Global storage
CLASS_LOADER_PUBLIC
BaseToFactoryMapMap & getGlobalPluginBaseToFactoryMapMap();
CLASS_LOADER_PUBLIC
LibraryVector & getLoadedLibraryVector();
CLASS_LOADER_PUBLIC
std::string getCurrentlyLoadingLibraryName();
CLASS_LOADER_PUBLIC
void setCurrentlyLoadingLibraryName(const std::string & library_name);
CLASS_LOADER_PUBLIC
ClassLoader * getCurrentlyActiveClassLoader();
CLASS_LOADER_PUBLIC
void setCurrentlyActiveClassLoader(ClassLoader * loader);
CLASS_LOADER_PUBLIC
FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_name);
template<typename Base>
FactoryMap & getFactoryMapForBaseClass()
{
return getFactoryMapForBaseClass(typeid(Base).name());
}
CLASS_LOADER_PUBLIC
MetaObjectGraveyardVector & getMetaObjectGraveyard();
CLASS_LOADER_PUBLIC
std::recursive_mutex & getLoadedLibraryVectorMutex();
CLASS_LOADER_PUBLIC
std::recursive_mutex & getPluginBaseToFactoryMapMapMutex();
CLASS_LOADER_PUBLIC
bool hasANonPurePluginLibraryBeenOpened();
CLASS_LOADER_PUBLIC
void hasANonPurePluginLibraryBeenOpened(bool hasIt);
template<typename Base>
using DeleterType = std::function<void (Base *)>;
template<typename Base>
using UniquePtr = std::unique_ptr<Base, DeleterType<Base>>;
// Plugin Functions
template<typename Derived, typename Base>
UniquePtr<AbstractMetaObjectBase>
registerPlugin(const std::string & class_name, const std::string & base_class_name)
{
// Note: This function will be automatically invoked when a dlopen() call
// opens a library. Normally it will happen within the scope of loadLibrary(),
// but that may not be guaranteed.
CONSOLE_BRIDGE_logDebug(
"class_loader.impl: "
"Registering plugin factory for class = %s, ClassLoader* = %p and library name %s.",
class_name.c_str(), getCurrentlyActiveClassLoader(),
getCurrentlyLoadingLibraryName().c_str());
if (nullptr == getCurrentlyActiveClassLoader()) {
CONSOLE_BRIDGE_logDebug(
"%s",
"class_loader.impl: ALERT!!! "
"A library containing plugins has been opened through a means other than through the "
"class_loader or pluginlib package. "
"This can happen if you build plugin libraries that contain more than just plugins "
"(i.e. normal code your app links against). "
"This inherently will trigger a dlopen() prior to main() and cause problems as class_loader "
"is not aware of plugin factories that autoregister under the hood. "
"The class_loader package can compensate, but you may run into namespace collision problems "
"(e.g. if you have the same plugin class in two different libraries and you load them both "
"at the same time). "
"The biggest problem is that library can now no longer be safely unloaded as the "
"ClassLoader does not know when non-plugin code is still in use. "
"In fact, no ClassLoader instance in your application will be unable to unload any library "
"once a non-pure one has been opened. "
"Please refactor your code to isolate plugins into their own libraries.");
hasANonPurePluginLibraryBeenOpened(true);
}
// Create factory
UniquePtr<AbstractMetaObjectBase> new_factory(
new impl::MetaObject<Derived, Base>(class_name, base_class_name),
[](AbstractMetaObjectBase * p) {
getPluginBaseToFactoryMapMapMutex().lock();
MetaObjectGraveyardVector & graveyard = getMetaObjectGraveyard();
for (auto iter = graveyard.begin(); iter != graveyard.end(); ++iter) {
if (*iter == p) {
graveyard.erase(iter);
break;
}
}
BaseToFactoryMapMap & factory_map_map = getGlobalPluginBaseToFactoryMapMap();
bool erase_flag = false;
for (auto & factory_map_item : factory_map_map) {
FactoryMap & factory_map = factory_map_item.second;
for (auto iter = factory_map.begin(); iter != factory_map.end(); ++iter) {
if (iter->second == p) {
factory_map.erase(iter);
erase_flag = true;
break;
}
}
if (erase_flag) {
break;
}
}
getPluginBaseToFactoryMapMapMutex().unlock();
#ifndef _WIN32
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#endif
delete (p); // Note: This is the only place where metaobjects can be destroyed
#ifndef _WIN32
#pragma GCC diagnostic pop
#endif
});
new_factory->addOwningClassLoader(getCurrentlyActiveClassLoader());
new_factory->setAssociatedLibraryPath(getCurrentlyLoadingLibraryName());
// Add it to global factory map map
getPluginBaseToFactoryMapMapMutex().lock();
FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
if (factoryMap.find(class_name) != factoryMap.end()) {
CONSOLE_BRIDGE_logWarn(
"class_loader.impl: SEVERE WARNING!!! "
"A namespace collision has occurred with plugin factory for class %s. "
"New factory will OVERWRITE existing one. "
"This situation occurs when libraries containing plugins are directly linked against an "
"executable (the one running right now generating this message). "
"Please separate plugins out into their own library or just don't link against the library "
"and use either class_loader::ClassLoader/MultiLibraryClassLoader to open.",
class_name.c_str());
}
factoryMap[class_name] = new_factory.get();
getPluginBaseToFactoryMapMapMutex().unlock();
CONSOLE_BRIDGE_logDebug(
"class_loader.impl: "
"Registration of %s complete (Metaobject Address = %p)",
class_name.c_str(), reinterpret_cast<void *>(new_factory.get()));
return new_factory;
}
template<typename Base>
Base * createInstance(const std::string & derived_class_name, ClassLoader * loader)
{
AbstractMetaObject<Base> * factory = nullptr;
getPluginBaseToFactoryMapMapMutex().lock();
FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
if (factoryMap.find(derived_class_name) != factoryMap.end()) {
factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]);
} else {
CONSOLE_BRIDGE_logError(
"class_loader.impl: No metaobject exists for class type %s.", derived_class_name.c_str());
}
getPluginBaseToFactoryMapMapMutex().unlock();
Base * obj = nullptr;
if (factory != nullptr && factory->isOwnedBy(loader)) {
obj = factory->create();
}
if (nullptr == obj) { // Was never created
if (factory && factory->isOwnedBy(nullptr)) {
CONSOLE_BRIDGE_logDebug(
"%s",
"class_loader.impl: ALERT!!! "
"A metaobject (i.e. factory) exists for desired class, but has no owner. "
"This implies that the library containing the class was dlopen()ed by means other than "
"through the class_loader interface. "
"This can happen if you build plugin libraries that contain more than just plugins "
"(i.e. normal code your app links against) -- that intrinsically will trigger a dlopen() "
"prior to main(). "
"You should isolate your plugins into their own library, otherwise it will not be "
"possible to shutdown the library!");
obj = factory->create();
} else {
throw class_loader::CreateClassException(
"Could not create instance of type " + derived_class_name);
}
}
CONSOLE_BRIDGE_logDebug(
"class_loader.impl: Created instance of type %s and object pointer = %p",
(typeid(obj).name()), obj);
return obj;
}
template<typename Base>
std::vector<std::string> getAvailableClasses(const ClassLoader * loader)
{
std::lock_guard<std::recursive_mutex> lock(getPluginBaseToFactoryMapMapMutex());
FactoryMap & factory_map = getFactoryMapForBaseClass<Base>();
std::vector<std::string> classes;
std::vector<std::string> classes_with_no_owner;
for (auto & it : factory_map) {
AbstractMetaObjectBase * factory = it.second;
if (factory->isOwnedBy(loader)) {
classes.push_back(it.first);
} else if (factory->isOwnedBy(nullptr)) {
classes_with_no_owner.push_back(it.first);
}
}
// Added classes not associated with a class loader (Which can happen through
// an unexpected dlopen() to the library)
classes.insert(classes.end(), classes_with_no_owner.begin(), classes_with_no_owner.end());
return classes;
}
CLASS_LOADER_PUBLIC
std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * loader);
CLASS_LOADER_PUBLIC
bool isLibraryLoaded(const std::string & library_path, const ClassLoader * loader);
CLASS_LOADER_PUBLIC
bool isLibraryLoadedByAnybody(const std::string & library_path);
CLASS_LOADER_PUBLIC
void loadLibrary(const std::string & library_path, ClassLoader * loader);
CLASS_LOADER_PUBLIC
void unloadLibrary(const std::string & library_path, ClassLoader * loader);
} // namespace impl
} // namespace class_loader
#endif // CLASS_LOADER__CLASS_LOADER_CORE_HPP_