Как прибивать программу с OpenMP ?
От: Кодт Россия  
Дата: 09.04.14 16:14
Оценка:
Дано: консольная программа, портируемая под винды и линукс.

Если нажать 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 << " ";
        }
    }
}
Перекуём баги на фичи!
Re: Как прибивать программу с OpenMP ?
От: smeeld  
Дата: 09.04.14 16:26
Оценка: +1
Здравствуйте, Кодт, Вы писали:



А если завершать каждый поток искуственно?
K> uint8_t run=1;

K> void halt_zuruck(int){ run=0; };


K> signal(SIGINT, halt_zuruck);


К> #pragma omp parallel

К> {
К> int n = omp_get_thread_num();
К> int x = n+1;
К> std::chrono::milliseconds dura( n==0 ? 5000 : 500 );
К> while(run)
К> {
К> 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 << " ";

К> }
К> }
К>}
К>[/c]
?
Re: Как прибивать программу с OpenMP ?
От: Vain Россия google.ru
Дата: 09.04.14 16:32
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Вопрос: как грамотно и портабельно убивать программу с omp, — при том, что нет задачи аккуратно джойнить потоки.

Как это нет?
Должна быть команда либо ожидания либо останова всех омп потоков.

К>Второй вопрос: как она вообще может работать после exit() ?!

А как по-твоему обычные потоки работают? Также и работают, программист не ставить ожидания закрытия потоков при выходе из первого потока, память под данными потоков овобждается и тут бац — акссес виолйшн или ещё что — поток то не остановлен был.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Как прибивать программу с OpenMP ?
От: ononim  
Дата: 09.04.14 16:40
Оценка:
К>Вопрос: как грамотно и портабельно убивать программу с omp, — при том, что нет задачи аккуратно джойнить потоки. Просто чтобы она молча останавливалась.
К>Второй вопрос: как она вообще может работать после exit() ?!
Это уже не грамотно, поскольку выход по exit() перед непосредственно самокилянием процесса производит разрушение статических объектов и всяких структур рантайма, которые внезапно могут заюзаться другими потоками до того как exit() дойдет до самоубиения процесса. А не грамотно самый безболезненный способ самоубиться под виндой — TerminateProcess(GetCurrentProcess(), 0);
Как много веселых ребят, и все делают велосипед...
Re: Как прибивать программу с OpenMP ?
От: MT-Wizard Украина  
Дата: 09.04.14 16:41
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Дано: консольная программа, портируемая под винды и линукс.


К>...


К сожалению, вариантов особо-то и нету: или добавлять флаг, проверяемый в цикле, или ExitProcess в винде — не знаю что там в никсах.
Опенмп вообще не затачивался на взаимодействие с рабочими потоками
А ти, москалику, вже приїхав (с)
Re[2]: Как прибивать программу с OpenMP ?
От: ononim  
Дата: 09.04.14 16:48
Оценка:
MW>К сожалению, вариантов особо-то и нету: или добавлять флаг, проверяемый в цикле, или ExitProcess в винде — не знаю что там в никсах.
MW>Опенмп вообще не затачивался на взаимодействие с рабочими потоками
ExitProcess может не помочь — он dll thread detach'и и FLS колбяки дергает. Так что только хардкор Terminate
Как много веселых ребят, и все делают велосипед...
Re[2]: Как прибивать программу с OpenMP ?
От: Кодт Россия  
Дата: 09.04.14 18:28
Оценка:
Здравствуйте, smeeld, Вы писали:

S>А если завершать каждый поток искуственно?


Много мороки, хотя это и более правильно.
Числодробилка на то и числодробилка, что может очень глубоко задумываться.
Хотелось что-то такое кавалерийское, раз — и вдребезги пополам.
Перекуём баги на фичи!
Re[2]: Как прибивать программу с OpenMP ?
От: Кодт Россия  
Дата: 09.04.14 18:33
Оценка:
Здравствуйте, ononim, Вы писали:

O>Это уже не грамотно, поскольку выход по exit() перед непосредственно самокилянием процесса производит разрушение статических объектов и всяких структур рантайма, которые внезапно могут заюзаться другими потоками до того как exit() дойдет до самоубиения процесса. А не грамотно самый безболезненный способ самоубиться под виндой — TerminateProcess(GetCurrentProcess(), 0);


Ну вот, казалось бы, terminate() должен делать то же самое, только портабельно.
Ан нет, — под виндой он
— не сразу убивает рабочие потоки
— вызывает какие-то панические реакции — системный месседжбокс про проблемы и интернет.

exit() — если бы не тупил на статических объектах — подошёл бы идеально.
Перекуём баги на фичи!
Re: Как прибивать программу с OpenMP ?
От: Vain Россия google.ru
Дата: 10.04.14 01:25
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Дано: консольная программа, портируемая под винды и линукс.

Так это, а кто мешает проэнумировать все омп потоки вручную в процедуре для ctrl-c, засуспендить и терминировать их, а потом ждать пока все не омп потоки не остановятся? Если какие-то не омп потоки ждут омп потоки, то терминация омп потоков должна затриггерить ожидание и не омп потоки долны закрыться (в идеале конечно).
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[3]: Как прибивать программу с OpenMP ?
От: SkyDance Земля  
Дата: 10.04.14 04:16
Оценка:
O>ExitProcess может не помочь — он dll thread detach'и и FLS колбяки дергает. Так что только хардкор Terminate

Можно отстреливать по частям: перечислить созданные процессом потоки, грохнуть их TerminateThread, потом аккуратно завершить процесс с хорошим exit code. Однако TerminateThread (и TerminateProcess(self) тоже) может зависнуть, если в момент терминирования он находится внутри какой-нибудь критической секции, которая не освобождается, но затем требуется в другом потоке.

Простейший пример: два потока пишут в консоль printf/cout, если один из них грохнуть в момент, когда он пишет — другой поток при попытке что-то вывести на консоль тупо зависнет. И уж обязательно зависнет, если попытаться вызвать exit (деаллокация консоли виснет).

С OpenMP я последний раз работал лет 10 назад, и уже в то время там использовались примитивы синхронизации.
Re: Как прибивать программу с OpenMP ?
От: savitar  
Дата: 10.04.14 06:37
Оценка:
Здравствуйте, Кодт, Вы писали:

К>...


попробуй из обработчика сигнала звать _exit(), это ничем не лучше TerminateProcess(), но портабельно.
Re: Как прибивать программу с OpenMP ?
От: ioni Россия  
Дата: 10.04.14 08:16
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Дано: консольная программа, портируемая под винды и линукс.


можно попробовать сохранять ид потока ну и потом соответсвенно прибивать
через какой нибудь терминайте
Re[2]: Как прибивать программу с OpenMP ?
От: Кодт Россия  
Дата: 10.04.14 08:19
Оценка:
Здравствуйте, ioni, Вы писали:

I>можно попробовать сохранять ид потока ну и потом соответсвенно прибивать

I>через какой нибудь терминайте

Сигнал всегда прилетает в нулевой поток, а terminate() меня не устраивает из-за дебильно-услужливой реакции виндоуза.
Перекуём баги на фичи!
Re[4]: Как прибивать программу с OpenMP ?
От: ononim  
Дата: 10.04.14 09:21
Оценка:
SD>Можно отстреливать по частям: перечислить созданные процессом потоки, грохнуть их TerminateThread, потом аккуратно завершить процесс с хорошим exit code.
Вот в момент 'аккуратного завершения' все и повиснет

SD>Однако TerminateThread (и TerminateProcess(self) тоже) может зависнуть, если в момент терминирования он находится внутри какой-нибудь критической секции, которая не освобождается, но затем требуется в другом потоке.

TerminateThread (и TerminateProcess( сами по себе не повиснут, ибо они — прямые вызовы сервисов ядра. Юзермодного кода в них минимум — собственно сам вызов сервиса ядра. Никаких критических секций там и в помине нету.
Как много веселых ребят, и все делают велосипед...
Re[3]: Как прибивать программу с OpenMP ?
От: niXman Ниоткуда https://github.com/niXman
Дата: 10.04.14 09:34
Оценка: :)
Здравствуйте, Кодт, Вы писали:

насколько мне известно, в С++ нельзя звать 'exit()' и подобные, ибо констекст процесса будет подпорчен еще до того, как вся необходимая С++-лабуда почистится.

вариантов два:
1. в обработчике сигнала устанавливай флаг и в рабочих потоках проверяй его.
2. в обработчике сигнала, шли своему процессу SIGKILL.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: Как прибивать программу с OpenMP ?
От: niXman Ниоткуда https://github.com/niXman
Дата: 10.04.14 09:41
Оценка: 68 (1)
Здравствуйте, niXman, Вы писали:

X>2. в обработчике сигнала, шли своему процессу SIGKILL.

в псевдокоде это так:
void halt_zuruck(int s)
{
    signal(SIGKILL, SIG_DFL);
    signal(SIGINT, SIG_DFL);
    signal(SIGTERM, SIG_DFL);
    cerr << "halted with code " << s << " in thread " << omp_get_thread_num() << endl;

    kill(getpid(), SIGKILL);
}
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: Как прибивать программу с OpenMP ?
От: niXman Ниоткуда https://github.com/niXman
Дата: 10.04.14 09:48
Оценка:
Здравствуйте, niXman, Вы писали:

X>насколько мне известно, в С++ нельзя звать 'exit()' и подобные, ибо констекст процесса будет подпорчен еще до того, как вся необходимая С++-лабуда почистится.

вот что говорят:

Note that objects with automatic storage are not destroyed by calling exit (C++).

пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: Как прибивать программу с OpenMP ?
От: ononim  
Дата: 10.04.14 10:24
Оценка: 9 (1) +1
К>Ну вот, казалось бы, terminate() должен делать то же самое, только портабельно.
К>Ан нет, — под виндой он
К>- не сразу убивает рабочие потоки
К>- вызывает какие-то панические реакции — системный месседжбокс про проблемы и интернет.
К>exit() — если бы не тупил на статических объектах — подошёл бы идеально.
В жизни каждого С программиста наступает момент, когда приходится писать #ifdef _WIN32/__APPLE__/whatever
Как много веселых ребят, и все делают велосипед...
Re[3]: Как прибивать программу с OpenMP ?
От: McQwerty Россия  
Дата: 10.04.14 10:36
Оценка: 95 (4)
Здравствуйте, Кодт, Вы писали:

К>Сигнал всегда прилетает в нулевой поток, а terminate() меня не устраивает из-за дебильно-услужливой реакции виндоуза.


    SetErrorMode (SEM_NOGPFAULTERRORBOX);


Достаточно для Release-сборки, чтобы не вылетало окна с предложением "решить проблему".
Re[4]: Как прибивать программу с OpenMP ?
От: Кодт Россия  
Дата: 10.04.14 11:46
Оценка: :))
Здравствуйте, ononim, Вы писали:

O>В жизни каждого С программиста наступает момент, когда приходится писать #ifdef _WIN32/__APPLE__/whatever





ой, флаг Украины
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.