Нормально ли для статической инициализации C++ дважды появляться в одной и той же трассировке?
Я пытаюсь отладить программу на C++, скомпилированную с GCC, которая зависает при запуске. Мьютекс GCC защищает статические локальные переменные функции, и кажется, что ожидание получения такой блокировки - вот почему она зависает. Как это происходит, довольно запутанно. Происходит статическая инициализация первого модуля A (есть функции __static_init, которые GCC вызывает, которые видны в обратном следе), которая вызывает функцию Foo(), которая имеет статическую локальную переменную. Статическая локальная переменная - это объект, который конструктор вызывает через несколько уровней функций, затем внезапно обратная трассировка имеет несколько символов, а затем происходит статическая инициализация второго модуля B (функции __static возникают снова и снова), который затем вызывает Foo(), но так как Foo () никогда не возвращался в первый раз, мьютекс локальной статической переменной все еще установлен, и он блокируется.
Как одна статическая инициация может вызвать другую? Моей первой теорией были общие библиотеки - этот модуль A будет вызывать некоторую функцию в модуле B, которая вызовет загрузку модуля B, вызывая, таким образом, статический init B, но это не так. Модуль A вообще не использует модуль B. Итак, у меня есть второе (и ужасающее) предположение. Скажи это:
Модуль A использует некоторую шаблонную функцию или функцию в шаблонном классе, например
foo<int>::bar()
Модуль B также использует
foo<int>::bar()
Модуль A вообще не зависит от модуля B
Во время ссылки у компоновщика есть два экземпляра
foo<int>::bar()
, но это нормально, потому что функции шаблона помечены как слабые символы...Во время выполнения модуль A вызывает
foo<int>::bar
и запускается статическая инициализация модуля B, даже если модуль B не зависит от модуля A! Зачем? Потому что компоновщик решил использовать экземпляр модуля foo::bar модуля B вместо экземпляра модуля A во время ссылки.
Этот сценарий действителен? Или статический init одного модуля никогда не должен запускать статический init в другом модуле?
Пояснение: GCC автоматически создает мьютексы для защиты любой статической переменной функции. Я ничего не делаю с мьютексами сам. Это способ GCC сделать функции статических переменных потокобезопасными.
Обновление: я знаю, что статическая инициализация не определена между единицами перевода и что я не должен зависеть от заказа. Но мне любопытно, если это нормальное поведение, как ключ к устранению проблемы. Это нормально для компилятора генерировать код, который делает это, или это потенциально указывает на ошибку в GCC?
2 ответа
Добро пожаловать в "фиаско статического порядка инициализации". Вероятно, вам следует просто прочитать всю эту статью, так как в ней будет подробно описано, как вы можете столкнуться с этой проблемой и как ее исправить.
Билл вытаскивает Эффективный C++ Пункт 4:
порядок инициализации нелокальных статических объектов, определенных в разных единицах перевода, не определен
Проще говоря, компилятору разрешено делать все, что он хочет.