Нормально ли для статической инициализации C++ дважды появляться в одной и той же трассировке?

Я пытаюсь отладить программу на C++, скомпилированную с GCC, которая зависает при запуске. Мьютекс GCC защищает статические локальные переменные функции, и кажется, что ожидание получения такой блокировки - вот почему она зависает. Как это происходит, довольно запутанно. Происходит статическая инициализация первого модуля A (есть функции __static_init, которые GCC вызывает, которые видны в обратном следе), которая вызывает функцию Foo(), которая имеет статическую локальную переменную. Статическая локальная переменная - это объект, который конструктор вызывает через несколько уровней функций, затем внезапно обратная трассировка имеет несколько символов, а затем происходит статическая инициализация второго модуля B (функции __static возникают снова и снова), который затем вызывает Foo(), но так как Foo () никогда не возвращался в первый раз, мьютекс локальной статической переменной все еще установлен, и он блокируется.

Как одна статическая инициация может вызвать другую? Моей первой теорией были общие библиотеки - этот модуль A будет вызывать некоторую функцию в модуле B, которая вызовет загрузку модуля B, вызывая, таким образом, статический init B, но это не так. Модуль A вообще не использует модуль B. Итак, у меня есть второе (и ужасающее) предположение. Скажи это:

  1. Модуль A использует некоторую шаблонную функцию или функцию в шаблонном классе, например foo<int>::bar()

  2. Модуль B также использует foo<int>::bar()

  3. Модуль A вообще не зависит от модуля B

  4. Во время ссылки у компоновщика есть два экземпляра foo<int>::bar(), но это нормально, потому что функции шаблона помечены как слабые символы...

  5. Во время выполнения модуль A вызывает foo<int>::barи запускается статическая инициализация модуля B, даже если модуль B не зависит от модуля A! Зачем? Потому что компоновщик решил использовать экземпляр модуля foo::bar модуля B вместо экземпляра модуля A во время ссылки.

Этот сценарий действителен? Или статический init одного модуля никогда не должен запускать статический init в другом модуле?

Пояснение: GCC автоматически создает мьютексы для защиты любой статической переменной функции. Я ничего не делаю с мьютексами сам. Это способ GCC сделать функции статических переменных потокобезопасными.

Обновление: я знаю, что статическая инициализация не определена между единицами перевода и что я не должен зависеть от заказа. Но мне любопытно, если это нормальное поведение, как ключ к устранению проблемы. Это нормально для компилятора генерировать код, который делает это, или это потенциально указывает на ошибку в GCC?

2 ответа

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

Билл вытаскивает Эффективный C++ Пункт 4:

порядок инициализации нелокальных статических объектов, определенных в разных единицах перевода, не определен

Проще говоря, компилятору разрешено делать все, что он хочет.

Другие вопросы по тегам