diff --git a/include/boost/archive/impl/archive_serializer_map.ipp b/include/boost/archive/impl/archive_serializer_map.ipp index 7f163ec407..98e058ddec 100644 --- a/include/boost/archive/impl/archive_serializer_map.ipp +++ b/include/boost/archive/impl/archive_serializer_map.ipp @@ -47,10 +47,10 @@ archive_serializer_map::insert(const basic_serializer * bs){ template BOOST_ARCHIVE_OR_WARCHIVE_DECL void archive_serializer_map::erase(const basic_serializer * bs){ - BOOST_ASSERT(! boost::serialization::singleton< - extra_detail::map - >::is_destroyed() - ); + // note: previously this conditional was a runtime assertion with + // BOOST_ASSERT. We've changed it because we've discovered that at + // least one platform is not guaranteed to destroy singletons in + // reverse order of distruction. if(boost::serialization::singleton< extra_detail::map >::is_destroyed()) diff --git a/include/boost/serialization/singleton.hpp b/include/boost/serialization/singleton.hpp index 2cf91833db..22949aecc3 100644 --- a/include/boost/serialization/singleton.hpp +++ b/include/boost/serialization/singleton.hpp @@ -9,6 +9,8 @@ // Copyright Robert Ramey 2007. Changes made to permit // application throughout the serialization library. // +// Copyright Alexander Grund 2018. Corrections to singleton lifetime +// // 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) @@ -32,7 +34,7 @@ // MS compatible compilers support #pragma once #if defined(_MSC_VER) # pragma once -#endif +#endif #include #include @@ -48,8 +50,8 @@ # pragma warning(disable : 4511 4512) #endif -namespace boost { -namespace serialization { +namespace boost { +namespace serialization { ////////////////////////////////////////////////////////////////////// // Provides a dynamically-initialized (singleton) instance of T in a @@ -58,7 +60,7 @@ namespace serialization { // details. // -// singletons created by this code are guarenteed to be unique +// Singletons created by this code are guaranteed to be unique // within the executable or shared library which creates them. // This is sufficient and in fact ideal for the serialization library. // The singleton is created when the module is loaded and destroyed @@ -74,14 +76,22 @@ namespace serialization { // Second, it provides a mechanism to detect when a non-const function // is called after initialization. -// make a singleton to lock/unlock all singletons for alteration. +// Make a singleton to lock/unlock all singletons for alteration. // The intent is that all singletons created/used by this code // are to be initialized before main is called. A test program -// can lock all the singletons when main is entereed. This any -// attempt to retieve a mutable instances while locked will -// generate a assertion if compiled for debug. - -// note usage of BOOST_DLLEXPORT. These functions are in danger of +// can lock all the singletons when main is entered. Thus any +// attempt to retrieve a mutable instance while locked will +// generate an assertion if compiled for debug. + +// The singleton template can be used in 2 ways: +// 1 (Recommended): Publicly inherit your type T from singleton, +// make its ctor protected and access it via T::get_const_instance() +// 2: Simply access singleton without changing T. Note that this only +// provides a global instance accesible by singleton::get_const_instance() +// or singleton::get_mutable_instance() to prevent using multiple instances +// of T make its ctor protected + +// Note on usage of BOOST_DLLEXPORT: These functions are in danger of // being eliminated by the optimizer when building an application in // release mode. Usage of the macro is meant to signal the compiler/linker // to avoid dropping these functions which seem to be unreferenced. @@ -113,34 +123,67 @@ static inline singleton_module & get_singleton_module(){ return m; } +namespace detail { + +// This is the class actually instantiated and hence the real singleton. +// So there will only be one instance of this class. This does not hold +// for singleton as a class derived from singleton could be +// instantiated multiple times. +// It also provides a flag `is_destroyed` which returns true, when the +// class was destructed. It is static and hence accesible even after +// destruction. This can be used to check, if the singleton is still +// accesible e.g. in destructors of other singletons. +template +class singleton_wrapper : public T +{ + static bool & get_is_destroyed(){ + // Prefer a static function member to avoid LNK1179. + // Note: As this is for a singleton (1 instance only) it must be set + // never be reset (to false)! + static bool is_destroyed_flag = false; + return is_destroyed_flag; + } +public: + singleton_wrapper(){ + BOOST_ASSERT(! is_destroyed()); + } + ~singleton_wrapper(){ + get_is_destroyed() = true; + } + static bool is_destroyed(){ + return get_is_destroyed(); + } +}; + +} // detail + template class singleton { private: - // note presumption that T has a default constructor - static T & m_instance; + static T * m_instance; // include this to provoke instantiation at pre-execution time static void use(T const &) {} static T & get_instance() { - static T t; - - // refer to instance, causing it to be instantiated (and - // initialized at startup on working compilers) BOOST_ASSERT(! is_destroyed()); + // use a wrapper so that types T with protected constructors can be used + // Using a static function member avoids LNK1179 + static detail::singleton_wrapper< T > t; + // note that the following is absolutely essential. // commenting out this statement will cause compilers to fail to // construct the instance at pre-execution time. This would prevent // our usage/implementation of "locking" and introduce uncertainty into - // the sequence of object initializaition. - use(m_instance); + // the sequence of object initialization. + use(* m_instance); return static_cast(t); } +protected: + // Do not allow instantiation of a singleton. But we want to allow + // `class T: public singleton` so we can't delete this ctor + BOOST_DLLEXPORT singleton(){} - static bool & get_is_destroyed(){ - static bool is_destroyed; - return is_destroyed; - } public: BOOST_DLLEXPORT static T & get_mutable_instance(){ BOOST_ASSERT(! get_singleton_module().is_locked()); @@ -150,18 +193,14 @@ class singleton { return get_instance(); } BOOST_DLLEXPORT static bool is_destroyed(){ - return get_is_destroyed(); - } - BOOST_DLLEXPORT singleton(){ - get_is_destroyed() = false; - } - BOOST_DLLEXPORT ~singleton() { - get_is_destroyed() = true; + return detail::singleton_wrapper< T >::is_destroyed(); } }; +// Assigning the instance reference to a static member forces initialization +// at startup time as described in http://tinyurl.com/ljdp8 template -T & singleton< T >::m_instance = singleton< T >::get_instance(); +T * singleton< T >::m_instance = & singleton< T >::get_instance(); } // namespace serialization } // namespace boost diff --git a/src/extended_type_info.cpp b/src/extended_type_info.cpp index 364fe56d84..573336c864 100644 --- a/src/extended_type_info.cpp +++ b/src/extended_type_info.cpp @@ -125,7 +125,10 @@ BOOST_SERIALIZATION_DECL void extended_type_info::key_unregister() const{ if(NULL == get_key()) return; - BOOST_ASSERT(! singleton::is_destroyed()); + // note: it's been discovered that at least one platform is not guaranteed + // to destroy singletons reverse order of construction. So we can't + // use a runtime assert here. Leave this in a reminder not to do this! + // BOOST_ASSERT(! singleton::is_destroyed()); if(! singleton::is_destroyed()){ detail::ktmap & x = singleton::get_mutable_instance(); detail::ktmap::iterator start = x.lower_bound(this); diff --git a/src/extended_type_info_typeid.cpp b/src/extended_type_info_typeid.cpp index 7413d2e3a4..91ace4c36e 100644 --- a/src/extended_type_info_typeid.cpp +++ b/src/extended_type_info_typeid.cpp @@ -95,7 +95,11 @@ BOOST_SERIALIZATION_DECL void extended_type_info_typeid_0::type_unregister() { if(NULL != m_ti){ - BOOST_ASSERT(! singleton::is_destroyed()); + // note: previously this conditional was a runtime assertion with + // BOOST_ASSERT. We've changed it because we've discovered that at + // least one platform is not guaranteed to destroy singletons in + // reverse order of distruction. + // BOOST_ASSERT(! singleton::is_destroyed()); if(! singleton::is_destroyed()){ tkmap & x = singleton::get_mutable_instance(); diff --git a/test/A.ipp b/test/A.ipp index f6c542104b..552e5fa19b 100644 --- a/test/A.ipp +++ b/test/A.ipp @@ -57,4 +57,4 @@ void A::serialize( #ifndef BOOST_NO_STD_WSTRING ar & BOOST_SERIALIZATION_NVP(z); #endif -}; +}