diff --git a/src/extended_type_info_typeid.cpp b/src/extended_type_info_typeid.cpp index a42725cd1..535083d75 100644 --- a/src/extended_type_info_typeid.cpp +++ b/src/extended_type_info_typeid.cpp @@ -102,14 +102,16 @@ extended_type_info_typeid_0::type_unregister() // BOOST_ASSERT(! singleton::is_destroyed()); if(! singleton::is_destroyed()){ tkmap & x = singleton::get_mutable_instance(); - - // remove all entries in map which corresponds to this type - // make sure that we don't use any invalidated iterators - while(true){ - const tkmap::iterator & it = x.find(this); - if(it == x.end()) + tkmap::iterator start = x.lower_bound(this); + const tkmap::iterator end = x.upper_bound(this); + // Entries compare equal when they describe the same type, and + // another module may well have registered that same type. Erase + // this entry alone, as key_unregister does. + for(; start != end; ++start){ + if(this == *start){ + x.erase(start); break; - x.erase(it); + } } } } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b79fb1576..7711379a8 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -155,6 +155,7 @@ if ! $(BOOST_ARCHIVE_LIST) { [ test-bsl-run test_dll_base_pointer : : dll_polymorphic_base dll_polymorphic_derived2 : static:no ] # [ test-bsl-run test_dll_plugin : : dll_derived2 : static:no linux:-ldl ] + [ test-bsl-run test_duplicate_type_registration ] [ test-bsl-run test_private_ctor ] [ test-bsl-run test_reset_object_address : A ] [ test-bsl-run test_void_cast ] diff --git a/test/test_duplicate_type_registration.cpp b/test/test_duplicate_type_registration.cpp new file mode 100644 index 000000000..7ffb3e36a --- /dev/null +++ b/test/test_duplicate_type_registration.cpp @@ -0,0 +1,64 @@ +/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 +// test_duplicate_type_registration.cpp + +// Copyright 2026 Gennaro Prota. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for updates, documentation, and revision history. + +// Every module linked against the library registers the types it serializes, +// so the type registry holds one entry per module per type. A module going +// away has to take its own entry with it and leave the others alone, or +// serialization stops working in the modules still running. + +// Reported by gast128 in +// https://github.com/boostorg/serialization/issues/325, from two COM +// components in separate DLLs registering the same type: unloading one of +// them made the other throw unregistered_class. Both the diagnosis and the +// remedy in the report were right. Thanks for the careful write up! + +// singleton explicitly allows a class derived from it to be instantiated +// more than once, which is what stands in for the second module below. + +#include + +// The lightweight test of Boost.Core is used here in place of +// test_tools.hpp, whose main locks the singleton module while the test runs, +// so that the type registry cannot change. Loading and unloading a module +// changes it while the program runs, which is the very thing under test. +#include + +#include +#include + +struct base { + virtual ~base(){} +}; + +struct derived : base { +}; + +typedef boost::serialization::extended_type_info_typeid eti_derived; + +int +main(){ + const eti_derived & first = + boost::serialization::singleton::get_const_instance(); + + const derived d; + BOOST_TEST(NULL != first.get_derived_extended_type_info(d)); + + { + // A second module registers the same type. + const eti_derived second; + BOOST_TEST(NULL != first.get_derived_extended_type_info(d)); + BOOST_TEST(NULL != second.get_derived_extended_type_info(d)); + } + // The second module is unloaded here. + + BOOST_TEST(NULL != first.get_derived_extended_type_info(d)); + + return boost::report_errors(); +}