-
Notifications
You must be signed in to change notification settings - Fork 96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix factory map leak (#131) #179
Conversation
Signed-off-by: y-okumura <y-okumura@isp.co.jp>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RE: dummy method. It appears to no longer be necessary if the destructor is virtual
.
|
||
static std::shared_ptr<BaseToFactoryMapMap> instance; | ||
|
||
// TODO: need lock? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@y-okumura-isp perhaps, static initialization is not thread-safe either.
static std::shared_ptr<BaseToFactoryMapMap> instance; | ||
|
||
// TODO: need lock? | ||
// TODO: we may use unique_ptr but use shared_ptr for PoC |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@y-okumura-isp std::unique_ptr
sounds best. Also, the custom deleter is fine, using std::unique_ptr<MetaObject>
values would be best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for comment.
I intended to make changes minimum. But it looks better to hold smart pointer directory.
I have one confirmation.
std::unique_ptr values would be best.
What you say is to change
typedef std::map<ClassName, impl::AbstractMetaObjectBase *> FactoryMap;
to
typedef std::map<ClassName, std::unique_ptr<impl::AbstractMetaObjectBase>> FactoryMap;
Right?
The pointer FactoryMap[class]
is used in createInstance
, so we could shared_ptr
.
typedef std::map<ClassName, std::shared_ptr<impl::AbstractMetaObjectBase>> FactoryMap;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right?
Correct.
The pointer FactoryMap[class] is used in createInstance, so we could shared_ptr.
I presume you mean
factory = dynamic_cast<impl::AbstractMetaObject<Base> *>(factoryMap[derived_class_name]); |
@@ -69,7 +69,7 @@ class CLASS_LOADER_PUBLIC AbstractMetaObjectBase | |||
* TEMPLATE SUBCLASSES, OTHERWISE THEY WILL PULL IN A REDUNDANT METAOBJECT | |||
* DESTRUCTOR OUTSIDE OF libclass_loader WITHIN THE PLUGIN LIBRARY! T | |||
*/ | |||
~AbstractMetaObjectBase(); | |||
virtual ~AbstractMetaObjectBase(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@y-okumura-isp meta: the comment is rather imprecise. Lacking virtual
indeed breaks polymorphic destruction.
Also, it's been many years but I'll ask. @mirzashah do you recall any of this? |
Is it better to update PR? |
Please do. Also, check the linter issues reported in the Rpr job. |
Hmm, it looks more complicated than I initially thought. I tried to use a unique pointer and found the following. Pointers in FatoryMap are not deleted directly but inserted into Additionally, according to the comment class_loader_core.cpp:399, I wonder if it is the right way to delete pointers in the destructor. |
I cancel this PR. (1) leaks in BaseToFactoryMapMap. I found Graveyard also has the same leak. We may fix (1) by a custom deleter or smart pointers.
Additionally, I initially referred to the leaks of
|
Fixes #131.
Sorry that my message is very long.
And as we dont't know the historical reasons well, I'm sorry if there are mistakes.
where leak happens
It looks 'new_factory' is not deleted when
factoryMap
is deleted.Additionally,
factoryMap
is finally a typedef ofstd::map
and is stored instatic BaseToFactoryMapMap instance
which is alsostd::map
.It looks
new_factory
is not deleted wheninstance
is out of scope.We tried to use a custom deleter(please see our draft PR), then we got
deleting object of polymorphic class type ‘class_loader::impl::AbstractMetaObjectBase’ which has non-virtual destructor might cause undefined behavior
warning. This may be because the AbstractMetaObjectBase destructor is not virtual.We found "virtual" was kept in PR53, but we didn't catch well why this should not be virtual.
About AbstractMetaObjectBase non-virtual destructor
new_factory
above is a variable ofimpl::AbstractMetaObject
, which has vtable but has non-virtual distructor.This looks to invoke undefined behavior according to https://en.cppreference.com/w/cpp/language/destructor "Virtual destructors".
And this is what the compiler warning says.
Here is the AbstractMetaObjectsBase code:
In the destructor comment, we can see
THIS MUST NOT BE VIRTUAL AND OVERIDDEN BY TEMPLATE SUBCLASSES
.We found this comment was added at da86427.
The commit message is
Fixed bug with redundant destructor definition being pulled into plugin library for metaobjects instead of being contained with libclass_loader.so
.Ths problem looks MetaObject destructor is placed in both
libsome_plugin.so
andlibclass_loader.so
.As there are three related classes and two runtime libraries(plugin and libclass_loader), let me explain the details.
We describe AbstractMetaObjectBase and its children structures as below in descendants to ancestor order:
When we implement a plugin library, we use
CLASS_LOADER_REGISTER_CLASS
macro in the plugin .cpp file.This macro calls
registerPlugin
and then invokesnew impl::MetaObject<Derived, Base>
.So (1) and (2) are instantiated in plugin .cpp files, thus object codes are in
libsome_plugin.so
.On the other hand, as
meta_object.cpp
is compiled only inlibclass_loader.so
, object code of (3) is included only inlibclass_loader.so
.As template classes are instantiated in plugin .cpp, the reasonable situation may be the following:
~MetaObject
and~AbstractMetaObject
are only in plugin library(libsome_plugin.so
) and not inlibclass_loader.so
~AbstractMetaObjectBase
is only inlibclass_loader.so
and not inlibsome_plugin.so
class_loader/src/meta_object.cpp
to compilelibsome_plugin.so
, this also looks always true.compare the latest code and our code
We built the latest code as
build-asan
and our patched code asbuild-asan2
.Here are the destructor symbols defined in
libclass_loader.so
.~AbstractMetaObject
and~MetaObject
is not defined.And here are destrucotr symbols in
libplugin.so
. We choosecomposition/libclient_component.so
.Only our version has
~AbstractMetaObjectBase
and~MetaObject
but they are only inlibclient_component.so
and not inlibclass_loader.so
.Note we can find
~AbstractMetaObjectBase()
but it's undefined symbol so it is not also redundant.Test result
By this modification, we can resolve ASAN error.
For example, in
composition/test_linktime_composition
, we got the following errors.With our modification, there are no error.
One one more topic: dummyMethod
We also found
AbstractMetaObjectBase::dummyMethod
is in bothlibplugin.so
andlibclass_loader.so
.This may be because implemantation of
dummyMethod
is in header file.Is it better to move it to .cpp file?