Opened 13 years ago

Closed 13 years ago

Last modified 9 years ago

#3014 closed Bugs (fixed)

Assertion on unregistering of extended_type_info

Reported by: mazay0@… 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)

extended_type_info.diff (889 bytes ) - added by mazay0@… 13 years ago.
fix for extended_type_info.cpp
minimal_example_boost3014.zip (2.2 KB ) - added by Mazay0@… 13 years ago.
Minimal example to reproduce the issue

Download all attachments as: .zip

Change History (13)

by mazay0@…, 13 years ago

Attachment: extended_type_info.diff added

fix for extended_type_info.cpp

comment:1 by Robert Ramey, 13 years ago

Status: newassigned

comment:2 by Robert Ramey, 13 years ago

Resolution: fixed
Status: assignedclosed

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 Mazay0@…, 13 years ago

Minimal example to reproduce the issue

in reply to:  2 comment:3 by mazay0@…, 13 years ago

Resolution: fixed
Status: closedreopened

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 Mazay0@…, 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 Robert Ramey, 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.

comment:6 by Robert Ramey, 13 years ago

Resolution: worksforme
Status: reopenedclosed

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

in reply to:  6 comment:7 by Mazay0@…, 13 years ago

Resolution: worksforme
Status: closedreopened

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 eric.woodruff@…, 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.

comment:9 by Robert Ramey, 13 years ago

Resolution: fixed
Status: reopenedclosed

I believe that I've checked into the trunk changes which resolve this issue.

in reply to:  9 comment:10 by eric.woodruff@…, 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 mazay0@…, 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.

Note: See TracTickets for help on using tickets.