Хитрая ошибка при использовании OpenMP в функции, загруженной из динамических библиотек

Мой вопрос касается использования OpenMP в функциях C++, хранящихся в динамических библиотеках. Рассмотрим следующий код (в shared.cpp):

#include "omp.h"
#include <iostream>
extern "C" {
int test() { 
  int N = omp_get_max_threads();
#pragma omp parallel num_threads(N)
  {
    std::cout << omp_get_thread_num() << std::endl;
  }
  return 0;
}
};

Я компилирую этот код с помощью g++: g++ -fopenmp -shared -fPIC -o shared.so shared.cpp. Затем, чтобы использовать функцию test, у меня есть следующая программа (main.cpp):

#include <iostream>
#include <dlfcn.h>
int main() {
  void* handle = dlopen("./shared.so", RTLD_NOW);
  if (!handle) {
    std::cerr << "can not open shared.so" << std::endl;
    return 1;
  }
  int(*f)() =  (int(*)()) dlsym(handle,"test");
  if (!f) {
    std::cerr << "can not find 'test' symbol in shared.so" << std::endl;
    return 1;
  }
  (*f)();
  if (dlclose(handle)) {
    std::cerr << "can not close shared.so" << std::endl;
    return 1;
  }  
  return 0;
}

компилируется с помощью команды: g++ -o main main.cpp -ldl Проблема в том, что ошибка сегментации возникает в самом конце выполнения программы. По словам valgrind, на данный момент некоторые потоки все еще активны, что, похоже, согласуется с поведением OpenMP.

Одно решение (для кода C) из this post состоит в том, чтобы скомпилируйте программу, используя флаг gcc -fopenmp, но g++ кажется достаточно умным, чтобы обнаружить, что OpenMP никогда не используется в этой программе, и среда OpenMP никогда не загружается (код сборки обеих версий одинаков) . Единственный обходной путь, который я нашел, - это сделать бесполезный вызов OpenMP в программе, что заставляет g++ загрузить среду OpenMP, и тогда выполнение будет правильным. Но для меня этот обходной путь довольно уродлив. Я пробовал g++-4.8.2, g++-4.8.1, g++-4.7.3 и g++-4.6.4. (В icc-14 использование опции -openmp в программе действительно решает проблему).

Кто-нибудь когда-нибудь сталкивался с этой проблемой? Есть ли более чистое обходное решение? Спасибо, Томас

Изменить Пробовал с G++-4.9.2: по-прежнему не получается


person ThomasI    schedule 18.03.2015    source источник


Ответы (2)


Я думаю, вы видите проблему с libgomp, библиотекой времени выполнения OpenMP для GCC. Попробуйте связать с ним: g++ -o main main.cpp -ldl -lgomp и ваш segfault исчезнет.

libgomp имеет некоторое внутреннее состояние, которое инициализируется при первом вызове OpenMP. По какой-то причине деинициализация не происходит, если вы динамически загружаете библиотеку OpenMP. Это звучит как ошибка для меня.

Компилятор Intel имеет собственную среду выполнения OpenMP (libiomp5), в которой нет этой проблемы.

person wpoely86    schedule 04.05.2015

У меня была очень похожая проблема, связанная с использованием dlopen с флагами RTLD_LAZY | RTLD_GLOBAL. Замена RTLD_LAZY на RTLD_NOW позволила этому работать.

Для вышеуказанной проблемы я предлагаю попробовать RTLD_NOW | RTLD_GLOBAL.

person Paul Price    schedule 01.07.2021