Skip to content

Commit 06d655d

Browse files
committed
Fix memory leaks and lifetime bugs
Signed-off-by: Tyler Weaver <[email protected]>
1 parent 332e35d commit 06d655d

File tree

11 files changed

+234
-205
lines changed

11 files changed

+234
-205
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ project(class_loader)
44

55
# Default to C++14
66
if(NOT CMAKE_CXX_STANDARD)
7-
set(CMAKE_CXX_STANDARD 14)
7+
set(CMAKE_CXX_STANDARD 17)
88
endif()
99
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
1010
add_compile_options(-Wall -Wextra -Wpedantic)

include/class_loader/class_loader.hpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ std::string systemLibraryFormat(const std::string & library_name);
7676
* definitions from which objects can be created/destroyed during runtime (i.e. class_loader).
7777
* Libraries loaded by a ClassLoader are only accessible within scope of that ClassLoader object.
7878
*/
79-
class ClassLoader
79+
class ClassLoader : public std::enable_shared_from_this<ClassLoader>
8080
{
8181
public:
8282
template<typename Base>
@@ -86,14 +86,17 @@ class ClassLoader
8686
using UniquePtr = std::unique_ptr<Base, DeleterType<Base>>;
8787

8888
/**
89-
* @brief Constructor for ClassLoader
89+
* @brief This is required (as apposed to the old constructor) to enforce that the lifetime of the ClassLoader
90+
* can be preserved by objects that this class creates.
9091
*
9192
* @param library_path - The path of the runtime library to load
9293
* @param ondemand_load_unload - Indicates if on-demand (lazy) unloading/loading of libraries
9394
* occurs as plugins are created/destroyed.
9495
*/
9596
CLASS_LOADER_PUBLIC
96-
explicit ClassLoader(const std::string & library_path, bool ondemand_load_unload = false);
97+
[[nodiscard]] static std::shared_ptr<ClassLoader> Make(
98+
const std::string & library_path,
99+
bool ondemand_load_unload = false);
97100

98101
/**
99102
* @brief Destructor for ClassLoader. All libraries opened by this ClassLoader are unloaded automatically.
@@ -109,7 +112,7 @@ class ClassLoader
109112
template<class Base>
110113
std::vector<std::string> getAvailableClasses() const
111114
{
112-
return class_loader::impl::getAvailableClasses<Base>(this);
115+
return class_loader::impl::getAvailableClasses<Base>(shared_from_this());
113116
}
114117

115118
/**
@@ -126,7 +129,9 @@ class ClassLoader
126129
{
127130
return std::shared_ptr<Base>(
128131
createRawInstance<Base>(derived_class_name, true),
129-
std::bind(&ClassLoader::onPluginDeletion<Base>, this, std::placeholders::_1)
132+
[class_loader = shared_from_this()](Base * obj) {
133+
return class_loader->onPluginDeletion<Base>(obj);
134+
}
130135
);
131136
}
132137

@@ -149,7 +154,9 @@ class ClassLoader
149154
Base * raw = createRawInstance<Base>(derived_class_name, true);
150155
return std::unique_ptr<Base, DeleterType<Base>>(
151156
raw,
152-
std::bind(&ClassLoader::onPluginDeletion<Base>, this, std::placeholders::_1)
157+
[class_loader = shared_from_this()](Base * obj) {
158+
return class_loader->onPluginDeletion<Base>(obj);
159+
}
153160
);
154161
}
155162

@@ -253,6 +260,16 @@ class ClassLoader
253260
int unloadLibrary();
254261

255262
private:
263+
/**
264+
* @brief Private Constructor for ClassLoader
265+
*
266+
* @param library_path - The path of the runtime library to load
267+
* @param ondemand_load_unload - Indicates if on-demand (lazy) unloading/loading of libraries
268+
* occurs as plugins are created/destroyed.
269+
*/
270+
CLASS_LOADER_PUBLIC
271+
explicit ClassLoader(const std::string & library_path, bool ondemand_load_unload = false);
272+
256273
/**
257274
* @brief Callback method when a plugin created by this class loader is destroyed
258275
*
@@ -324,7 +341,7 @@ class ClassLoader
324341
loadLibrary();
325342
}
326343

327-
Base * obj = class_loader::impl::createInstance<Base>(derived_class_name, this);
344+
Base * obj = class_loader::impl::createInstance<Base>(derived_class_name, shared_from_this());
328345
assert(obj != NULL); // Unreachable assertion if createInstance() throws on failure.
329346

330347
if (managed) {

include/class_loader/class_loader_core.hpp

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,14 @@ namespace impl
7676
typedef std::string LibraryPath;
7777
typedef std::string ClassName;
7878
typedef std::string BaseClassName;
79-
typedef std::map<ClassName, impl::AbstractMetaObjectBase *> FactoryMap;
79+
typedef std::map<ClassName, std::shared_ptr<AbstractMetaObjectBase>> FactoryMap;
8080
typedef std::map<BaseClassName, FactoryMap> BaseToFactoryMapMap;
8181
typedef std::pair<LibraryPath, std::shared_ptr<rcpputils::SharedLibrary>> LibraryPair;
8282
typedef std::vector<LibraryPair> LibraryVector;
83-
typedef std::vector<AbstractMetaObjectBase *> MetaObjectVector;
83+
typedef std::vector<std::shared_ptr<AbstractMetaObjectBase>> MetaObjectVector;
84+
85+
CLASS_LOADER_PUBLIC
86+
MetaObjectVector & getMetaObjectGraveyard();
8487

8588
CLASS_LOADER_PUBLIC
8689
void printDebugInfoToScreen();
@@ -139,8 +142,15 @@ ClassLoader * getCurrentlyActiveClassLoader();
139142
* @param loader - pointer to the currently active ClassLoader.
140143
*/
141144
CLASS_LOADER_PUBLIC
142-
void setCurrentlyActiveClassLoader(ClassLoader * loader);
145+
void setCurrentlyActiveClassLoader(std::shared_ptr<ClassLoader> loader);
143146

147+
/**
148+
* @brief Inserts meta object into the graveyard to preserve the lifetime.
149+
*
150+
* @param meta_obj - pointer to the meta object.
151+
*/
152+
CLASS_LOADER_PUBLIC
153+
void insertMetaObjectIntoGraveyard(std::shared_ptr<AbstractMetaObjectBase> meta_obj);
144154

145155
/**
146156
* @brief This function extracts a reference to the FactoryMap for appropriate base class out of
@@ -240,8 +250,8 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
240250
}
241251

242252
// Create factory
243-
impl::AbstractMetaObject<Base> * new_factory =
244-
new impl::MetaObject<Derived, Base>(class_name, base_class_name);
253+
auto new_factory =
254+
std::make_shared<impl::MetaObject<Derived, Base>>(class_name, base_class_name);
245255
new_factory->addOwningClassLoader(getCurrentlyActiveClassLoader());
246256
new_factory->setAssociatedLibraryPath(getCurrentlyLoadingLibraryName());
247257

@@ -260,13 +270,17 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
260270
"and use either class_loader::ClassLoader/MultiLibraryClassLoader to open.",
261271
class_name.c_str());
262272
}
273+
274+
// We insert every factory into the graveyard to preserve the lifetime of all meta objects
275+
// until the process exits.
276+
insertMetaObjectIntoGraveyard(new_factory);
263277
factoryMap[class_name] = new_factory;
264278
getPluginBaseToFactoryMapMapMutex().unlock();
265279

266280
CONSOLE_BRIDGE_logDebug(
267281
"class_loader.impl: "
268282
"Registration of %s complete (Metaobject Address = %p)",
269-
class_name.c_str(), reinterpret_cast<void *>(new_factory));
283+
class_name.c_str(), reinterpret_cast<void *>(new_factory.get()));
270284
}
271285

272286
/**
@@ -278,22 +292,22 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
278292
* @return A pointer to newly created plugin, note caller is responsible for object destruction
279293
*/
280294
template<typename Base>
281-
Base * createInstance(const std::string & derived_class_name, ClassLoader * loader)
295+
Base * createInstance(const std::string & derived_class_name, std::shared_ptr<ClassLoader> loader)
282296
{
283297
AbstractMetaObject<Base> * factory = nullptr;
284298

285299
getPluginBaseToFactoryMapMapMutex().lock();
286300
FactoryMap & factoryMap = getFactoryMapForBaseClass<Base>();
287301
if (factoryMap.find(derived_class_name) != factoryMap.end()) {
288-
factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]);
302+
factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name].get());
289303
} else {
290304
CONSOLE_BRIDGE_logError(
291305
"class_loader.impl: No metaobject exists for class type %s.", derived_class_name.c_str());
292306
}
293307
getPluginBaseToFactoryMapMapMutex().unlock();
294308

295309
Base * obj = nullptr;
296-
if (factory != nullptr && factory->isOwnedBy(loader)) {
310+
if (factory != nullptr && factory->isOwnedBy(loader.get())) {
297311
obj = factory->create();
298312
}
299313

@@ -333,7 +347,7 @@ Base * createInstance(const std::string & derived_class_name, ClassLoader * load
333347
* @return A vector of strings where each string is a plugin we can create
334348
*/
335349
template<typename Base>
336-
std::vector<std::string> getAvailableClasses(const ClassLoader * loader)
350+
std::vector<std::string> getAvailableClasses(std::shared_ptr<const ClassLoader> loader)
337351
{
338352
std::lock_guard<std::recursive_mutex> lock(getPluginBaseToFactoryMapMapMutex());
339353

@@ -342,8 +356,8 @@ std::vector<std::string> getAvailableClasses(const ClassLoader * loader)
342356
std::vector<std::string> classes_with_no_owner;
343357

344358
for (auto & it : factory_map) {
345-
AbstractMetaObjectBase * factory = it.second;
346-
if (factory->isOwnedBy(loader)) {
359+
auto factory = it.second;
360+
if (factory->isOwnedBy(loader.get())) {
347361
classes.push_back(it.first);
348362
} else if (factory->isOwnedBy(nullptr)) {
349363
classes_with_no_owner.push_back(it.first);
@@ -364,7 +378,8 @@ std::vector<std::string> getAvailableClasses(const ClassLoader * loader)
364378
* within a ClassLoader's visible scope
365379
*/
366380
CLASS_LOADER_PUBLIC
367-
std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * loader);
381+
std::vector<std::string> getAllLibrariesUsedByClassLoader(
382+
std::shared_ptr<const ClassLoader> loader);
368383

369384
/**
370385
* @brief Indicates if passed library loaded within scope of a ClassLoader.
@@ -375,7 +390,7 @@ std::vector<std::string> getAllLibrariesUsedByClassLoader(const ClassLoader * lo
375390
* @return true if the library is loaded within loader's scope, else false
376391
*/
377392
CLASS_LOADER_PUBLIC
378-
bool isLibraryLoaded(const std::string & library_path, const ClassLoader * loader);
393+
bool isLibraryLoaded(const std::string & library_path, std::shared_ptr<const ClassLoader> loader);
379394

380395
/**
381396
* @brief Indicates if passed library has been loaded by ANY ClassLoader

include/class_loader/multi_library_class_loader.hpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ namespace class_loader
5656
{
5757

5858
typedef std::string LibraryPath;
59-
typedef std::map<LibraryPath, class_loader::ClassLoader *> LibraryToClassLoaderMap;
60-
typedef std::vector<ClassLoader *> ClassLoaderVector;
59+
typedef std::map<LibraryPath, std::shared_ptr<class_loader::ClassLoader>> LibraryToClassLoaderMap;
60+
typedef std::vector<std::shared_ptr<ClassLoader>> ClassLoaderVector;
6161

6262
class MultiLibraryClassLoaderImpl;
6363

@@ -96,7 +96,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
9696
"class_loader::MultiLibraryClassLoader: "
9797
"Attempting to create instance of class type %s.",
9898
class_name.c_str());
99-
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
99+
std::shared_ptr<ClassLoader> loader = getClassLoaderForClass<Base>(class_name);
100100
if (nullptr == loader) {
101101
throw class_loader::CreateClassException(
102102
"MultiLibraryClassLoader: Could not create object of class type " +
@@ -121,7 +121,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
121121
std::shared_ptr<Base> createInstance(
122122
const std::string & class_name, const std::string & library_path)
123123
{
124-
ClassLoader * loader = getClassLoaderForLibrary(library_path);
124+
std::shared_ptr<ClassLoader> loader = getClassLoaderForLibrary(library_path);
125125
if (nullptr == loader) {
126126
throw class_loader::NoClassLoaderExistsException(
127127
"Could not create instance as there is no ClassLoader in "
@@ -146,7 +146,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
146146
CONSOLE_BRIDGE_logDebug(
147147
"class_loader::MultiLibraryClassLoader: Attempting to create instance of class type %s.",
148148
class_name.c_str());
149-
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
149+
std::shared_ptr<ClassLoader> loader = getClassLoaderForClass<Base>(class_name);
150150
if (nullptr == loader) {
151151
throw class_loader::CreateClassException(
152152
"MultiLibraryClassLoader: Could not create object of class type " + class_name +
@@ -170,7 +170,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
170170
ClassLoader::UniquePtr<Base>
171171
createUniqueInstance(const std::string & class_name, const std::string & library_path)
172172
{
173-
ClassLoader * loader = getClassLoaderForLibrary(library_path);
173+
std::shared_ptr<ClassLoader> loader = getClassLoaderForLibrary(library_path);
174174
if (nullptr == loader) {
175175
throw class_loader::NoClassLoaderExistsException(
176176
"Could not create instance as there is no ClassLoader in "
@@ -193,7 +193,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
193193
template<class Base>
194194
Base * createUnmanagedInstance(const std::string & class_name)
195195
{
196-
ClassLoader * loader = getClassLoaderForClass<Base>(class_name);
196+
std::shared_ptr<ClassLoader> loader = getClassLoaderForClass<Base>(class_name);
197197
if (nullptr == loader) {
198198
throw class_loader::CreateClassException(
199199
"MultiLibraryClassLoader: Could not create class of type " + class_name);
@@ -213,7 +213,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
213213
template<class Base>
214214
Base * createUnmanagedInstance(const std::string & class_name, const std::string & library_path)
215215
{
216-
ClassLoader * loader = getClassLoaderForLibrary(library_path);
216+
std::shared_ptr<ClassLoader> loader = getClassLoaderForLibrary(library_path);
217217
if (nullptr == loader) {
218218
throw class_loader::NoClassLoaderExistsException(
219219
"Could not create instance as there is no ClassLoader in MultiLibraryClassLoader "
@@ -273,7 +273,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
273273
template<class Base>
274274
std::vector<std::string> getAvailableClassesForLibrary(const std::string & library_path)
275275
{
276-
ClassLoader * loader = getClassLoaderForLibrary(library_path);
276+
std::shared_ptr<ClassLoader> loader = getClassLoaderForLibrary(library_path);
277277
if (nullptr == loader) {
278278
throw class_loader::NoClassLoaderExistsException(
279279
"There is no ClassLoader in MultiLibraryClassLoader bound to library " +
@@ -321,15 +321,15 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
321321
* @param library_path - the library from which we want to create the plugin
322322
* @return A pointer to the ClassLoader*, == nullptr if not found
323323
*/
324-
ClassLoader * getClassLoaderForLibrary(const std::string & library_path);
324+
std::shared_ptr<ClassLoader> getClassLoaderForLibrary(const std::string & library_path);
325325

326326
/// Gets a handle to the class loader corresponding to a specific class.
327327
/**
328328
* @param class_name name of class for which we want to create instance.
329329
* @return A pointer to the ClassLoader, or NULL if not found.
330330
*/
331331
template<typename Base>
332-
ClassLoader * getClassLoaderForClass(const std::string & class_name)
332+
std::shared_ptr<ClassLoader> getClassLoaderForClass(const std::string & class_name)
333333
{
334334
ClassLoaderVector loaders = getAllAvailableClassLoaders();
335335
for (ClassLoaderVector::iterator i = loaders.begin(); i != loaders.end(); ++i) {
@@ -355,7 +355,7 @@ class CLASS_LOADER_PUBLIC MultiLibraryClassLoader
355355
*/
356356
void shutdownAllClassLoaders();
357357

358-
MultiLibraryClassLoaderImpl * impl_;
358+
std::unique_ptr<MultiLibraryClassLoaderImpl> impl_;
359359
};
360360

361361

src/class_loader.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,22 @@
2929

3030
#include "class_loader/class_loader.hpp"
3131

32+
#include <memory>
3233
#include <string>
3334

3435
namespace class_loader
3536
{
3637

3738
bool ClassLoader::has_unmananged_instance_been_created_ = false;
3839

40+
std::shared_ptr<ClassLoader> ClassLoader::Make(
41+
const std::string & library_path,
42+
bool ondemand_load_unload)
43+
{
44+
// Not using make_shared because the constructor for ClassLoader is private
45+
return std::shared_ptr<ClassLoader>(new ClassLoader{library_path, ondemand_load_unload});
46+
}
47+
3948
bool ClassLoader::hasUnmanagedInstanceBeenCreated()
4049
{
4150
return ClassLoader::has_unmananged_instance_been_created_;
@@ -82,7 +91,7 @@ const std::string & ClassLoader::getLibraryPath() const
8291

8392
bool ClassLoader::isLibraryLoaded() const
8493
{
85-
return class_loader::impl::isLibraryLoaded(getLibraryPath(), this);
94+
return class_loader::impl::isLibraryLoaded(getLibraryPath(), shared_from_this());
8695
}
8796

8897
bool ClassLoader::isLibraryLoadedByAnyClassloader() const

0 commit comments

Comments
 (0)