Skip to content

Commit c427a50

Browse files
author
Chen Lihui
authored
make the meta-object alive in the lifecycle of the registered plugin (#201)
* make the meta-object alive only in the lifecycle of the registered plugin Signed-off-by: Chen Lihui <[email protected]> * remove unuseful variable passed in the lambda Signed-off-by: Chen Lihui <[email protected]> * add extra tests to make test cases exists in both unique ptr test and utest Signed-off-by: Chen Lihui <[email protected]> * use the original mutex rather than import a new mutex Signed-off-by: Chen Lihui <[email protected]> * update document comment Signed-off-by: Chen Lihui <[email protected]> * symbol visibility for Windows Signed-off-by: Chen Lihui <[email protected]> * add extra document comment Signed-off-by: Chen Lihui <[email protected]> * update doc Signed-off-by: Chen Lihui <[email protected]> Signed-off-by: Chen Lihui <[email protected]>
1 parent 6ba5c47 commit c427a50

File tree

5 files changed

+127
-42
lines changed

5 files changed

+127
-42
lines changed

include/class_loader/class_loader_core.hpp

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include <cstddef>
3636
#include <cstdio>
37+
#include <functional>
3738
#include <map>
3839
#include <memory>
3940
#include <mutex>
@@ -81,6 +82,20 @@ typedef std::map<BaseClassName, FactoryMap> BaseToFactoryMapMap;
8182
typedef std::pair<LibraryPath, std::shared_ptr<rcpputils::SharedLibrary>> LibraryPair;
8283
typedef std::vector<LibraryPair> LibraryVector;
8384
typedef std::vector<AbstractMetaObjectBase *> MetaObjectVector;
85+
class MetaObjectGraveyardVector : public MetaObjectVector
86+
{
87+
public:
88+
~MetaObjectGraveyardVector()
89+
{
90+
// Make sure not to access the pointer value in the static variable of `getMetaObjectGraveyard()
91+
// when destroying `meta_object` in the unique_ptr deleter. Because the static variable in
92+
// `getMetaObjectGraveyard()` can be destructed before the static variable
93+
// `g_register_plugin_ ## UniqueID` in some circumstances.
94+
// NOTE of the vector dtor in the STL: if the elements themselves are pointers, the pointed-to
95+
// memory is not touched in any way. Managing the pointer is the user's responsibility.
96+
clear();
97+
}
98+
};
8499

85100
CLASS_LOADER_PUBLIC
86101
void printDebugInfoToScreen();
@@ -164,6 +179,14 @@ FactoryMap & getFactoryMapForBaseClass()
164179
return getFactoryMapForBaseClass(typeid(Base).name());
165180
}
166181

182+
/**
183+
* @brief Gets a handle to a list of meta object of graveyard.
184+
*
185+
* @return A reference to the MetaObjectGraveyardVector contained within the meta object of graveyard.
186+
*/
187+
CLASS_LOADER_PUBLIC
188+
MetaObjectGraveyardVector & getMetaObjectGraveyard();
189+
167190
/**
168191
* @brief To provide thread safety, all exposed plugin functions can only be run serially by multiple threads.
169192
*
@@ -193,6 +216,12 @@ bool hasANonPurePluginLibraryBeenOpened();
193216
CLASS_LOADER_PUBLIC
194217
void hasANonPurePluginLibraryBeenOpened(bool hasIt);
195218

219+
template<typename Base>
220+
using DeleterType = std::function<void (Base *)>;
221+
222+
template<typename Base>
223+
using UniquePtr = std::unique_ptr<Base, DeleterType<Base>>;
224+
196225
// Plugin Functions
197226

198227
/**
@@ -205,9 +234,11 @@ void hasANonPurePluginLibraryBeenOpened(bool hasIt);
205234
* @param Derived - parameteric type indicating concrete type of plugin
206235
* @param Base - parameteric type indicating base type of plugin
207236
* @param class_name - the literal name of the class being registered (NOT MANGLED)
237+
* @return A class_loader::impl::UniquePtr<AbstractMetaObjectBase> to newly created meta object.
208238
*/
209239
template<typename Derived, typename Base>
210-
void registerPlugin(const std::string & class_name, const std::string & base_class_name)
240+
UniquePtr<AbstractMetaObjectBase>
241+
registerPlugin(const std::string & class_name, const std::string & base_class_name)
211242
{
212243
// Note: This function will be automatically invoked when a dlopen() call
213244
// opens a library. Normally it will happen within the scope of loadLibrary(),
@@ -240,8 +271,28 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
240271
}
241272

242273
// Create factory
243-
impl::AbstractMetaObject<Base> * new_factory =
244-
new impl::MetaObject<Derived, Base>(class_name, base_class_name);
274+
UniquePtr<AbstractMetaObjectBase> new_factory(
275+
new impl::MetaObject<Derived, Base>(class_name, base_class_name),
276+
[](AbstractMetaObjectBase * p) {
277+
getPluginBaseToFactoryMapMapMutex().lock();
278+
MetaObjectGraveyardVector & graveyard = getMetaObjectGraveyard();
279+
for (auto iter = graveyard.begin(); iter != graveyard.end(); ++iter) {
280+
if (*iter == p) {
281+
graveyard.erase(iter);
282+
break;
283+
}
284+
}
285+
getPluginBaseToFactoryMapMapMutex().unlock();
286+
287+
#ifndef _WIN32
288+
#pragma GCC diagnostic push
289+
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
290+
#endif
291+
delete (p); // Note: This is the only place where metaobjects can be destroyed
292+
#ifndef _WIN32
293+
#pragma GCC diagnostic pop
294+
#endif
295+
});
245296
new_factory->addOwningClassLoader(getCurrentlyActiveClassLoader());
246297
new_factory->setAssociatedLibraryPath(getCurrentlyLoadingLibraryName());
247298

@@ -260,13 +311,14 @@ void registerPlugin(const std::string & class_name, const std::string & base_cla
260311
"and use either class_loader::ClassLoader/MultiLibraryClassLoader to open.",
261312
class_name.c_str());
262313
}
263-
factoryMap[class_name] = new_factory;
314+
factoryMap[class_name] = new_factory.get();
264315
getPluginBaseToFactoryMapMapMutex().unlock();
265316

266317
CONSOLE_BRIDGE_logDebug(
267318
"class_loader.impl: "
268319
"Registration of %s complete (Metaobject Address = %p)",
269-
class_name.c_str(), reinterpret_cast<void *>(new_factory));
320+
class_name.c_str(), reinterpret_cast<void *>(new_factory.get()));
321+
return new_factory;
270322
}
271323

272324
/**

include/class_loader/register_macro.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@
5656
if (!std::string(Message).empty()) { \
5757
CONSOLE_BRIDGE_logInform("%s", Message); \
5858
} \
59-
class_loader::impl::registerPlugin<_derived, _base>(#Derived, #Base); \
59+
holder = class_loader::impl::registerPlugin<_derived, _base>(#Derived, #Base); \
6060
} \
61+
private: \
62+
class_loader::impl::UniquePtr<class_loader::impl::AbstractMetaObjectBase> holder; \
6163
}; \
6264
static ProxyExec ## UniqueID g_register_plugin_ ## UniqueID; \
6365
} // namespace

src/class_loader_core.cpp

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ FactoryMap & getFactoryMapForBaseClass(const std::string & typeid_base_class_nam
7272
return factoryMapMap[base_class_name];
7373
}
7474

75-
MetaObjectVector & getMetaObjectGraveyard()
75+
MetaObjectGraveyardVector & getMetaObjectGraveyard()
7676
{
77-
static MetaObjectVector instance;
77+
static MetaObjectGraveyardVector instance;
7878
return instance;
7979
}
8080

@@ -352,7 +352,7 @@ void revivePreviouslyCreateMetaobjectsFromGraveyard(
352352
const std::string & library_path, ClassLoader * loader)
353353
{
354354
std::lock_guard<std::recursive_mutex> b2fmm_lock(getPluginBaseToFactoryMapMapMutex());
355-
MetaObjectVector & graveyard = getMetaObjectGraveyard();
355+
MetaObjectGraveyardVector & graveyard = getMetaObjectGraveyard();
356356

357357
for (auto & obj : graveyard) {
358358
if (obj->getAssociatedLibraryPath() == library_path) {
@@ -373,14 +373,14 @@ void revivePreviouslyCreateMetaobjectsFromGraveyard(
373373
}
374374

375375
void purgeGraveyardOfMetaobjects(
376-
const std::string & library_path, ClassLoader * loader, bool delete_objs)
376+
const std::string & library_path, ClassLoader * loader)
377377
{
378378
MetaObjectVector all_meta_objs = allMetaObjects();
379379
// Note: Lock must happen after call to allMetaObjects as that will lock
380380
std::lock_guard<std::recursive_mutex> b2fmm_lock(getPluginBaseToFactoryMapMapMutex());
381381

382-
MetaObjectVector & graveyard = getMetaObjectGraveyard();
383-
MetaObjectVector::iterator itr = graveyard.begin();
382+
MetaObjectGraveyardVector & graveyard = getMetaObjectGraveyard();
383+
MetaObjectGraveyardVector::iterator itr = graveyard.begin();
384384

385385
while (itr != graveyard.end()) {
386386
AbstractMetaObjectBase * obj = *itr;
@@ -393,34 +393,7 @@ void purgeGraveyardOfMetaobjects(
393393
reinterpret_cast<void *>(loader),
394394
nullptr == loader ? "NULL" : loader->getLibraryPath().c_str());
395395

396-
bool is_address_in_graveyard_same_as_global_factory_map =
397-
std::find(all_meta_objs.begin(), all_meta_objs.end(), *itr) != all_meta_objs.end();
398396
itr = graveyard.erase(itr);
399-
if (delete_objs) {
400-
if (is_address_in_graveyard_same_as_global_factory_map) {
401-
CONSOLE_BRIDGE_logDebug(
402-
"%s",
403-
"class_loader.impl: "
404-
"Newly created metaobject factory in global factory map map has same address as "
405-
"one in graveyard -- metaobject has been purged from graveyard but not deleted.");
406-
} else {
407-
assert(hasANonPurePluginLibraryBeenOpened() == false);
408-
CONSOLE_BRIDGE_logDebug(
409-
"class_loader.impl: "
410-
"Also destroying metaobject %p (class = %s, base_class = %s, library_path = %s) "
411-
"in addition to purging it from graveyard.",
412-
reinterpret_cast<void *>(obj), obj->className().c_str(), obj->baseClassName().c_str(),
413-
obj->getAssociatedLibraryPath().c_str());
414-
#ifndef _WIN32
415-
#pragma GCC diagnostic push
416-
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
417-
#endif
418-
delete (obj); // Note: This is the only place where metaobjects can be destroyed
419-
#ifndef _WIN32
420-
#pragma GCC diagnostic pop
421-
#endif
422-
}
423-
}
424397
} else {
425398
itr++;
426399
}
@@ -484,16 +457,14 @@ void loadLibrary(const std::string & library_path, ClassLoader * loader)
484457
"Checking factory graveyard for previously loaded metaobjects...",
485458
library_path.c_str());
486459
revivePreviouslyCreateMetaobjectsFromGraveyard(library_path, loader);
487-
// Note: The 'false' indicates we don't want to invoke delete on the metaobject
488-
purgeGraveyardOfMetaobjects(library_path, loader, false);
489460
} else {
490461
CONSOLE_BRIDGE_logDebug(
491462
"class_loader.impl: "
492463
"Library %s generated new factory metaobjects on load. "
493464
"Destroying graveyarded objects from previous loads...",
494465
library_path.c_str());
495-
purgeGraveyardOfMetaobjects(library_path, loader, true);
496466
}
467+
purgeGraveyardOfMetaobjects(library_path, loader);
497468

498469
// Insert library into global loaded library vectory
499470
std::lock_guard<std::recursive_mutex> llv_lock(getLoadedLibraryVectorMutex());

test/unique_ptr_test.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,34 @@ TEST(ClassLoaderUniquePtrTest, basicLoad) {
5959
}
6060
}
6161

62+
TEST(ClassLoaderUniquePtrTest, basicLoadTwice) {
63+
try {
64+
{
65+
ClassLoader loader1(LIBRARY_1, false);
66+
loader1.createUniqueInstance<Base>("Cat")->saySomething(); // See if lazy load works
67+
}
68+
ClassLoader loader1(LIBRARY_1, false);
69+
loader1.createUniqueInstance<Base>("Cat")->saySomething(); // See if lazy load works
70+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
71+
SUCCEED();
72+
} catch (class_loader::ClassLoaderException & e) {
73+
FAIL() << "ClassLoaderException: " << e.what() << "\n";
74+
}
75+
}
76+
77+
TEST(ClassLoaderUniquePtrTest, basicLoadTwiceSameTime) {
78+
try {
79+
ClassLoader loader1(LIBRARY_1, false);
80+
loader1.createUniqueInstance<Base>("Cat")->saySomething(); // See if lazy load works
81+
ClassLoader loader2(LIBRARY_1, false);
82+
loader2.createUniqueInstance<Base>("Cat")->saySomething(); // See if lazy load works
83+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
84+
SUCCEED();
85+
} catch (class_loader::ClassLoaderException & e) {
86+
FAIL() << "ClassLoaderException: " << e.what() << "\n";
87+
}
88+
}
89+
6290
TEST(ClassLoaderUniquePtrTest, basicLoadFailures) {
6391
ClassLoader loader1(LIBRARY_1, false);
6492
ClassLoader loader2("", false);

test/utest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,38 @@ TEST(ClassLoaderTest, basicLoad) {
6767
SUCCEED();
6868
}
6969

70+
TEST(ClassLoaderTest, basicLoadTwice) {
71+
try {
72+
{
73+
class_loader::ClassLoader loader1(LIBRARY_1, false);
74+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
75+
loader1.createInstance<Base>("Cat")->saySomething(); // See if lazy load works
76+
}
77+
class_loader::ClassLoader loader1(LIBRARY_1, false);
78+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
79+
loader1.createInstance<Base>("Cat")->saySomething(); // See if lazy load works
80+
} catch (class_loader::ClassLoaderException & e) {
81+
FAIL() << "ClassLoaderException: " << e.what() << "\n";
82+
}
83+
84+
SUCCEED();
85+
}
86+
87+
TEST(ClassLoaderTest, basicLoadTwiceSameTime) {
88+
try {
89+
class_loader::ClassLoader loader1(LIBRARY_1, false);
90+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
91+
loader1.createInstance<Base>("Cat")->saySomething(); // See if lazy load works
92+
class_loader::ClassLoader loader2(LIBRARY_1, false);
93+
ASSERT_NO_THROW(class_loader::impl::printDebugInfoToScreen());
94+
loader2.createInstance<Base>("Cat")->saySomething(); // See if lazy load works
95+
} catch (class_loader::ClassLoaderException & e) {
96+
FAIL() << "ClassLoaderException: " << e.what() << "\n";
97+
}
98+
99+
SUCCEED();
100+
}
101+
70102
// Requires separate namespace so static variables are isolated
71103
TEST(ClassLoaderUnmanagedTest, basicLoadUnmanaged) {
72104
try {

0 commit comments

Comments
 (0)