Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions include/class_loader/class_loader_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,7 @@ using BaseToFactoryMapMap = std::map<BaseClassName, FactoryMap>;
using LibraryPair = std::pair<LibraryPath, std::shared_ptr<rcpputils::SharedLibrary>>;
using LibraryVector = std::vector<LibraryPair>;
using MetaObjectVector = std::vector<AbstractMetaObjectBase *>;
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();
}
};
using MetaObjectGraveyardVector = MetaObjectVector;

CLASS_LOADER_PUBLIC
void printDebugInfoToScreen();
Expand Down
32 changes: 18 additions & 14 deletions src/class_loader_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,33 +43,37 @@ namespace impl
{

// Global data
//
// These are allocated on first use and never destroyed. A ClassLoader or MultiLibraryClassLoader
// with static storage duration (e.g. a static pluginlib::ClassLoader) may be destroyed after
// them at exit, and still needs them to unload its libraries (ros/class_loader#33).

std::recursive_mutex & getLoadedLibraryVectorMutex()
{
static std::recursive_mutex m;
return m;
static auto * m = new std::recursive_mutex();
return *m;
}

std::recursive_mutex & getPluginBaseToFactoryMapMapMutex()
{
static std::recursive_mutex m;
return m;
static auto * m = new std::recursive_mutex();
return *m;
}

namespace
{
// Serializes loadLibrary()/unloadLibrary() so a dlopen() never overlaps a dlclose().
std::recursive_mutex & getLoaderMutex()
{
static std::recursive_mutex m;
return m;
static auto * m = new std::recursive_mutex();
return *m;
}
} // namespace

BaseToFactoryMapMap & getGlobalPluginBaseToFactoryMapMap()
{
static BaseToFactoryMapMap instance;
return instance;
static auto * instance = new BaseToFactoryMapMap();
return *instance;
}

FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_name)
Expand All @@ -81,20 +85,20 @@ FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_nam

MetaObjectGraveyardVector & getMetaObjectGraveyard()
{
static MetaObjectGraveyardVector instance;
return instance;
static auto * instance = new MetaObjectGraveyardVector();
return *instance;
}

LibraryVector & getLoadedLibraryVector()
{
static LibraryVector instance;
return instance;
static auto * instance = new LibraryVector();
return *instance;
}

std::string & getCurrentlyLoadingLibraryNameReference()
{
static std::string library_name;
return library_name;
static auto * library_name = new std::string();
return *library_name;
}

std::string getCurrentlyLoadingLibraryName()
Expand Down
22 changes: 22 additions & 0 deletions test/utest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*/

#include <cstddef>
#include <cstdlib>

#ifndef _WIN32
#include <dlfcn.h>
Expand Down Expand Up @@ -320,6 +321,27 @@ TYPED_TEST(MultiClassLoaderTest, instanceOutlivesLoader) {

// Tests that don't depend on how instances are managed.

// ros/class_loader#33: loaders with static storage duration, like a static pluginlib::ClassLoader,
// are destroyed at exit after class_loader's own globals were created, and must still be able to
// unload their libraries then.
TEST(ClassLoaderStaticTest, staticLoadersDestroyedAtExit) {
// Re-run the binary for the child, so the loaders below are created before class_loader's
// globals, as they would be in a program that has a static loader.
GTEST_FLAG_SET(death_test_style, "threadsafe");
EXPECT_EXIT(
{
static class_loader::MultiLibraryClassLoader multi_loader(false);
static class_loader::ClassLoader loader(LIBRARY_2, false);
multi_loader.loadLibrary(LIBRARY_1);
if (multi_loader.createInstance<Base>("Dog")->saySomething() != "Bark" ||
loader.createInstance<Base>("Robot")->saySomething() != "Beep boop")
{
std::exit(1);
}
std::exit(0);
}, ::testing::ExitedWithCode(0), "");
}

TEST(ClassLoaderBasicTest, basicLoadFailures) {
class_loader::ClassLoader loader1(LIBRARY_1, false);
class_loader::ClassLoader loader2("", false);
Expand Down
Loading