Порядок уничтожения членов статических функций в общих библиотеках - PullRequest
0 голосов
/ 27 апреля 2018

В настоящее время я изучаю очень сложную ошибку в Boost.Serialization, связанную с синглетонами. Для контекста: Boost 1.65 изменил реализацию синглтона, нарушающего уведомление is_destructed, что приводит к ошибкам при выходе из программы или выгрузке библиотеки. Boost 1.66 «исправил» это, но утечка памяти.

Синглтон-код (соответствующий этому вопросу) сводится к следующему:

template<class T> struct singleton{
    T& inst(){
        static T t;
        return t;
    }
}

Использование статической переменной-функции-члена позволяет избежать статического фиаско init init, но все равно имеет ту же проблему с уничтожением.

Однако Поиск проблем порядка статической инициализации C ++ показывает код, как решить эту проблему: когда Ctor A использует B, тогда B будет создан первым и, следовательно, уничтожен последним. Это также указано в Порядок уничтожения статических объектов в C ++ . (completion of the destructor happens in the reverse order of the completion of the constructor)

Пока все хорошо. Boost.Serialization теперь использует несколько синглетов типа extended_type_info_typeid<T> для регистрации некоторых метаданных типа пользователя T в другом синглтоне std::multiset<const bs::typeid_system::extended_type_info_typeid_0*,...>. Это делается с помощью multiset (предположим, что все синглеты отсюда) из конструктора extended_type_info_typeid_0. В деструкторе extended_type_info_typeid_0 запись в multiset удалена.

Это означает, что мы имеем именно ту ситуацию, которая описана выше, и multiset должен пережить другие случаи.

Это ломается при использовании общих библиотек. У меня есть следующий тестовый пример:

test_multi_singleton.cpp:

int f();
int g();

int main(int argc, char**){
  // Make sure symbols are used
  if(argc==8) return f();
  if(argc==9) return g();
}

multi_singleton1.cpp:
#include <boost/serialization/extended_type_info_typeid.hpp>

int f(){
  return 0 != boost::serialization::extended_type_info_typeid<int>::get_const_instance().get_key();
} 

multi_singleton2.cpp:
#include <boost/serialization/extended_type_info_typeid.hpp>

int g(){
  // Use different(!) type
  return 0 != boost::serialization::extended_type_info_typeid<float>::get_const_instance().get_key();
} 

Build with:

g++ multi_singleton1.cpp -lboost_serialization -fPIC -shared -olibmulti_singleton1.so
g++ multi_singleton2.cpp -lboost_serialization -fPIC -shared -olibmulti_singleton2.so
g++ test_multi_singleton.cpp -L. -lmulti_singleton1 -lmulti_singleton2

Run in valgrind:
valgrind ./a.out

Видно, что это повреждает память в Boost 1.65. Причиной является испорченный порядок, который я отслеживал, угоняя и регистрируя вызовы ctor / dtor:

ctor 0x7f9f0aa074a0 std::multiset<const extended_type_info_typeid_0*>
ctor 0x7f9f0a7f63e0 extended_type_info_typeid<float>

ctor 0x7f9f0a7f64a0 std::multiset<const extended_type_info_typeid_0*>
ctor 0x7f9f0aa073e0 extended_type_info_typeid<int>

dtor 0x7f9f0aa073e0 extended_type_info_typeid<int>
dtor 0x7f9f0aa074a0 std::multiset<const extended_type_info_typeid_0*>
dtor 0x7f9f0a7f64a0 std::multiset<const extended_type_info_typeid_0*>
dtor 0x7f9f0a7f63e0 extended_type_info_typeid<float>

Это использует GCC 6.4, но то же самое с GCC 7.1. Как видите, 2 мультимножества уничтожаются вместе и до 2-го extended_type_info_typeid.

Я что-то упустил? Это разрешено стандартом C ++?

1 Ответ

0 голосов
/ 27 апреля 2018

С basic.start.term / 3 :

Если завершение конструктора или динамическая инициализация объект со статической продолжительностью хранения сильно случается до того другой, завершение деструктора второго секвенируется до инициации деструктора первого.

Также:

Для объекта массива или типа класса все подобъекты этого объекта уничтожено до любого объекта области блока со статической продолжительностью хранения инициализированный при построении подобъектов уничтожается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...