#3014 closed Bugs (fixed)
Assertion on unregistering of extended_type_info
Reported by: | Owned by: | Robert Ramey | |
---|---|---|---|
Milestone: | Boost 1.39.0 | Component: | serialization |
Version: | Boost Release Branch | Severity: | Problem |
Keywords: | Cc: |
Description
/With boost version 1.39.0 I got the following behavior. On exit from the program an assertion assert(NULL != l) in extended_type_info.cpp:47 failed. After some debugging I have revealed that same instances of extended_type_info were registered by extended_type_info::key_register() several times (about 3-4 times each). I am not sure if it is normal. The program which I debugged, staticaly links a library, and this library includes same headers with serializable classes as the program. Anyway, the multimap returned by singleton<detail::ktmap>::get_mutable_instance() may contain extended_type_info instances with the same m_key. BUT in extended_type_info::key_register():103, after removal of type info from the multimap, the value of m_key is being NULLed (m_key = NULL). Therefore, if this instance of extended_type_info is contained in the multimap at least once, it will make the next call of lower_bound to fail, because it has NULL in its m_key.
As a temporary fix I have commented out the the following strings assert(start != end); and m_key = NULL; I removed the assertion because now the check in extended_type_info destructor may not work and key_unregister() method may be called for an instance allready removed from the multiset.
I suppose that multiple registration can be caused by anonymous namespace introduced in export.hpp file around guid_initializer, but I didn't make deep investigations.
The initial issue was observed on Win-32 + mingw-gcc.4.3
and on
Ubuntu-Linux-64 + gcc.4.3
Attachments (2)
Change History (13)
comment:1 by , 13 years ago
Status: | new → assigned |
---|
follow-up: 3 comment:2 by , 13 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
I have made changes to extended_type_info.cpp and void_cast.cpp in the trunk. I can't really test these very well. I would appreciate it if you could rebuild the library with these latest files and try again.
Robert Ramey
by , 13 years ago
Attachment: | minimal_example_boost3014.zip added |
---|
Minimal example to reproduce the issue
comment:3 by , 13 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Replying to ramey:
...
Unfortunately this fix doesn't help. I've managed to create a minimal example which repoduce the bug (see attach https://svn.boost.org/trac/boost/attachment/ticket/3014/minimal_example_boost3014.zip). Put breakpoints in key_register() and key_unregister() methods to meditate on the issue. As far as I understand the key_register() method is being invoked by guid_initializer from every translation unit which includes BOOST_CLASS_EXPORT_GUID macro. So two pointers to the same instance of extended_type_info appears in ktmap multiset. When key_unregister() is beig called first time everything works fine, but only one pointer is being erased from ktmap. The other one stays there and points to extended_type_info object with nulled m_key field. So, when key_unregister() is called at second time (no mater for which type), the lower_bound() call blows up on this mine.
comment:4 by , 13 years ago
At first sight there is three possible fixes: 1) Additional check in key_register() to avoid duplicates in ktmap (Also I don't understand why ktmap is a multiset in spite of a simple set). 2) Remove break; from key_unregister() and null m_key only after erasing of all occurences of this extended_type_info. 3) Split declaration and definition of guid_initializer. Make two macroses BOOST_CLASS_EXPORT_GUID_DECLARE and BOOST_CLASS_EXPORT_GUID_DEFINE. This whould allow user to explicitly specify translational unit in which an instance of guid_initializer should be created.
comment:5 by , 13 years ago
"(Also I don't understand why ktmap is a multiset in spite of a simple set)."
THe problem can occur during the dynamic loading and unloading of DLLS where there the same code is instantiated in multiple modules. when a dll is unloaded, one has to take care that the correct registry entries are deleted so as not too leave any dangling pointers.
I believe this problem will not occure if BOOST_CLASS_EXPORT is only included inside an implementation file rather than a header.
I'm still looking at this.
follow-up: 7 comment:6 by , 13 years ago
Resolution: | → worksforme |
---|---|
Status: | reopened → closed |
I've built and tested this minimal example on my VC 7.1 system and it compiles, links and runs without error. Our differing results might be due to one of the following:
a) different development system b) I made a change sometime ago to address this. I think my local copy of extended type info is in sync with the trunk. Maybe we're out of sync with this.
Robert Ramey
comment:7 by , 13 years ago
Resolution: | worksforme |
---|---|
Status: | closed → reopened |
Replying to ramey:
I've built and tested this minimal example on my VC 7.1 system and it compiles, links and runs without error.
I have traced the exmaple on VC 7.1. It runs without errors because singleton<detail::ktmap> dies between destructions of extented_type_info instances for Foo and Bar classes (after Bar, before Foo). Therefore, the check if(! singleton<detail::ktmap>::is_destroyed()) in extended_type_info::key_unregister() saves the program from attempt to do something with ktmap, which contains "invalid" extented_type_info instance. AFAIU in this case destruction order is undefined. Hence I suppose that this is UB.
Is it safe to try to avoid multiple insertion of the same extended_type_info instances in ktmap? It seems that m_key value can be used as an indicator if this extended_type_info is registered in ktmap. It is used in this manner in ~extended_type_info(). May be we can do the similar check in key_register()?
BOOST_SERIALIZATION_DECL(void) extended_type_info::key_register(const char *key) { assert(NULL != key); if (m_key == NULL) { m_key = key; singleton<detail::ktmap>::get_mutable_instance().insert(this); } }
comment:8 by , 13 years ago
Mazay0, I've hit this same issue after updating to 1.39 and your suggestion of avoiding inserting multiple instances in ktmap in key_register fixes my issue.
follow-up: 10 comment:9 by , 13 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
I believe that I've checked into the trunk changes which resolve this issue.
comment:10 by , 13 years ago
Replying to ramey:
I believe that I've checked into the trunk changes which resolve this issue.
FYI - I hit this again on 1.40. I still need to apply the suggested patch by Mazzy0 to get a successful unit test run. I tried to find the changes on the trunk that fixed this issue, but couldn't. ramey, could you attach the patch to this bug?
comment:11 by , 9 years ago
Just in case: The problem is fixed since 1.41.0: http://www.boost.org/doc/libs/1_41_0/libs/serialization/doc/traits.html#export
This is addressed by invoking BOOST_CLASS_EXPORT_IMPLEMENT(T) in the file which defines (implements) the class T. This ensures that code for the derived class T will be explicity instantiated.
...
So in the serialization library, this is addressed by invoking BOOST_CLASS_EXPORT_KEY2(my_class, "my_class_external_identifier") in the header file which declares he class.
...
For programs which consist of only one module - that is programs which do not use DLLS, one can specify BOOST_CLASS_EXPORT(my_class) or BOOST_CLASS_EXPORT_GUID(my_class, "my_class_external_identifier") in either he declaration header or definition.