Am 09.11.18 um 06:50 schrieb degski via Boost:
I would like to request for someone involved in that discussion to write a tl;dr of that discussion, as it seems to touch upon a number of issues that are poorly understood [also by long-time cpp-war-heroes] in general. /r/cpp might also be a good place for this and [comments] could shed more light on this.
As the original author of the PR(s) I'll do this. Note that everything below is already explained in detail in the description of the PRs and initial issue: - https://github.com/boostorg/serialization/pull/111, https://github.com/boostorg/serialization/pull/128, https://github.com/boostorg/serialization/pull/129, https://github.com/boostorg/serialization/pull/131 https://github.com/boostorg/serialization/issues/105 There is also my question on SO: https://stackoverflow.com/questions/50064617/order-of-destruction-for-static... To summarize: There are 2 issues: 1. Wrong destruction order in shared libraries 2. Bug in Boost.Serialization (`singleton::is_destroyed`) on 1.: As per the C++ standard the destructors are called in reverse order of how the constructors finished. This is even true for static initialization (pre-main) and destruction (post-main) of global static instances which we have here. This means if type `Foo` accesses a type `Registry` in its constructor it is guaranteed that `Registry` will be constructed before `Foo` and destroyed after. Boost.Serialization makes use of this fact and lets `Foo` register itself in `Registry` in the ctor of `Foo` and unregister in the destructor of `Foo`. Now there is the case of shared libraries (see also https://github.com/boostorg/serialization/pull/131#discussion_r231498983): Assume 2 shared libraries build against static Boost, so each of them has separate instances of `Registry`, which is also ok. Now lib1 uses a type `Foo` which gets registered in lib1-`Registry` and lib2 uses a type `Bar` which gets registered in lib2-`Registry`. During unload/destruction `Foo` is destroyed first as before and lib1-`Registry` afterwards. But because shared library behaviour is implementation defined we now run into the following situation on Linux (not on OSX or Windows): With the destruction of lib1-`Registry` the runtime also destroys lib2-`Registry`, probably because they are of the same type. One can only guess what happens behind the scenes, but maybe a name-map is used or so. The problem: Now lib2-`Registry` is destroyed before `Bar` and when that accesses the `Registry` you get a use-after-free situation and a corruption. This can be handled (and was pre Boost 1.65) by a flag `is_destroyed` which gets set once the singleton destructor is called and checked in the destructors accessing another singleton. (Basically: `if(!Registry::is_destroyed()) Registry::unregister(this)`) but here issue 2 (introduced in 1.65): The singleton template can be used directly by **not** inheriting from it. In this case an instance of `T` is returned directly: https://github.com/boostorg/serialization/blob/boost-1.68.0/include/boost/se... BUT: The `is_destroyed` flag is set in the destructor of the singleton template: https://github.com/boostorg/serialization/blob/boost-1.68.0/include/boost/se... As `T` is not inherited from `singleton<T>` this destructor `~singleton<T>` is never called (because it is never constructed either) and hence the flag is never set. See https://github.com/boostorg/serialization/pull/131#discussion_r232088304 This is different when `T` is inherited from `singleton<T>` in which case the construction of `T` will lead to the construction of `singleton<T>` and also destruction which is why **in this case** `is_destroyed` works correctly. Due to this the issue 1. does not get caught and leads to the crash. The question on this thread was now on how to include the "crash test" into BJam: - We need to build 2 shared libraries - Link them into static boost As Boost is build statically BJam does not add `-fPIC` to its compile flags but as it is linked into shared libraries later this fails as addresses are fixed already. Although BJam has the information (link boost library into a shared library) it does not "backpropagate" this into the build of boost. See https://github.com/boostorg/serialization/pull/131#discussion_r231964634, https://github.com/boostorg/serialization/pull/131#issuecomment-437236135 Thanks to Peter Dimovs help we got Robert convinced to include my fix into the develop branch of Boost.Serialization: https://github.com/boostorg/serialization/commit/f297d80d6ef55ac66525320d047.... I now hope the tests get included to so it doesn't break in the future.