Дано: консольная программа, портируемая под винды и линукс.
Если нажать Ctrl+C или Ctrl+Break, или нажать на крестик у консоли, она крешится, виндоуз предлагает искать ошибку в интернете и всё такое.
Чтобы такого не было, добавил обработчик сигналов SIGINT, SIGTERM, SIGBREAK, — он пишет сообщение на экран и в журнал и вызывает exit(1)
Добавляю #pragma omp parallel for...
Программа в некоторых случаях завершается по exit(1), как и ожидалось, а в некоторых — рабочие потоки omp продолжают работать! Естественно, очень быстро они приходят к расстрелу памяти и дохнут по защите памяти.
Заменил exit на abort() или terminate() — та же фигня, некоторые потки продолжают работать после сигнала, только теперь виндоуз гарантированно предлагает искать ошибку.
Вопрос: как грамотно и портабельно убивать программу с omp, — при том, что нет задачи аккуратно джойнить потоки. Просто чтобы она молча останавливалась.
Второй вопрос: как она вообще может работать после exit() ?!
Программа — числодробилка, она загружает здоровенные бинарные файлы (под сотню мегабайт), долго их обрабатывает и пишет результат в текстовый файл.
Т.е. каждый поток проходит по такому циклу:
— 100% загрузка кернелмода (выделяет память; читает файл)
— 100% загрузка юзермода (считает)
— выдох — синхронизируется для записи в текстовый файл
Попробовал сделать дистиллированный пример такого вида — проблема не воспроизводится.
#include <csignal>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <cstdio>
#include <omp.h>
#include <chrono>
#include <thread>
using namespace std;
void halt_zuruck(int s)
{
cerr << "halted with code " << s << " in thread " << omp_get_thread_num() << endl;
exit(0);
}
char buf[1000000];
int main()
{
signal(SIGINT, halt_zuruck);
signal(SIGTERM, halt_zuruck);
signal(SIGBREAK, halt_zuruck); // это чисто виндовское - Ctrl+Break
cout << "go...";
#pragma omp parallel
{
int n = omp_get_thread_num();
int x = n+1;
std::chrono::milliseconds dura( n==0 ? 5000 : 500 );
while(true)
{
std::this_thread::sleep_for( dura );
for(int i=0; i<10000*(n+1); ++i)
{
FILE* f = fopen("x.cpp", "r"); // чтобы создать нагрузку на кернелмод
fread(buf, 1, sizeof(buf), f);
fclose(f);
}
for(int i=0; i<10000000; ++i)
x = x%2 ? x*3+1 : x/2; // чтобы создать нагрузку на юзермод
cout << n << " ";
}
}
}