Re[3]: Как прибивать программу с OpenMP ?
От: McQwerty Россия  
Дата: 10.04.14 10:36
Оценка: 95 (4)
Здравствуйте, Кодт, Вы писали:

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


    SetErrorMode (SEM_NOGPFAULTERRORBOX);


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

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





ой, флаг Украины
Перекуём баги на фичи!
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: Как прибивать программу с OpenMP ?
От: lxa http://aliakseis.livejournal.com
Дата: 23.04.14 12:42
Оценка: 68 (1)
Не знаю, в тему ли, но вдруг...
http://www.cplusplus.com/reference/cstdlib/quick_exit/
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[3]: Как прибивать программу с OpenMP ?
От: niXman Ниоткуда https://github.com/niXman
Дата: 10.04.14 09:34
Оценка: :)
Здравствуйте, Кодт, Вы писали:

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

вариантов два:
1. в обработчике сигнала устанавливай флаг и в рабочих потоках проверяй его.
2. в обработчике сигнала, шли своему процессу SIGKILL.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Как прибивать программу с 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 ?
От: 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[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: оффтоп
От: The Passenger СССР  
Дата: 11.04.14 13:23
Оценка:
Здравствуйте, Кодт, Вы писали:

К>

К>    signal(SIGINT,   halt_zuruck);
К>    signal(SIGTERM,  halt_zuruck);
К>    signal(SIGBREAK, halt_zuruck); // это чисто виндовское - Ctrl+Break

К>


а винда вообще ловит эти сигналы? ... а то у меня чета не очень получилось — толи не ловит совсем, толи на бряке не стопорится
по крайней мере на первых двух
Re[2]: оффтоп
От: Кодт Россия  
Дата: 11.04.14 13:42
Оценка:
Здравствуйте, The Passenger, Вы писали:

TP>а винда вообще ловит эти сигналы? ... а то у меня чета не очень получилось — толи не ловит совсем, толи на бряке не стопорится

TP>по крайней мере на первых двух

Я опытным путём выяснил, что ловит. SIGINT — это Ctrl+C, SIGBREAK — Ctrl+Break и закрывании консоли по [x], SIGTERM — не помню, как-то прилетал.
Просто в виндах есть два способа прервать программу
Перекуём баги на фичи!
Re[2]: Как прибивать программу с OpenMP ?
От: Vain Россия google.ru
Дата: 23.04.14 13:25
Оценка:
Здравствуйте, lxa, Вы писали:

lxa>Не знаю, в тему ли, но вдруг...

lxa>http://www.cplusplus.com/reference/cstdlib/quick_exit/
Что только не придумают, чтобы не писать правильный клинап.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Как прибивать программу с OpenMP ?
От: Vain Россия google.ru
Дата: 23.04.14 13:32
Оценка:
Здравствуйте, Кодт, Вы писали:

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

может попробывать что-нибудь такое: http://www.uni-kassel.de/eecs/fileadmin/datas/fb16/Fachgebiete/PLM/Dokumente/Research/OpenMP/thread-cancellation.html

Specification

The following new directives to support thread cancellation in
OpenMP are proposed:

#pragma omp cancelregion:
Asks all threads in the team to stop their parallel work and go
to the end of the parallel region, where only the master thread
will continue execution as usual. The emphasis here is on "asks".
The threads in the team are not cancelled immediately, but merely
a cancel flag is set for each of them (cooperative cancellation).
An exception is the thread that called the directive: it is
cancelled immediately by an implicit call of the exitregion
directive (explained below). It is the task of the programmer to
check if the cancel flag has been set, using a new OpenMP runtime
library function:

int omp_get_cancelled (void):
This function returns 1 (true), if the cancellation of the
enclosing parallel region was requested and 0 (false) otherwise.

#pragma omp exitregion:
This directive is not only useful for thread cancellation, but
can be used at any point in a parallel region to immediately end
the execution of the calling thread. This is accomplished by
jumping to the end of the present parallel region, right into its
closing implicit barrier (which is of course honored). Together,
both new directives can be used in the following way to achieve
thread cancellation:

cancelling thread:
#pragma omp cancelregion

all other threads in the team:
if (omp_get_cancelled ()) {

/* this jumps directly into the closing, implicit barrier
* at the end of the parallel region
*/
#pragma omp exitregion
}

...

[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Как прибивать программу с OpenMP ?
От: McQwerty Россия  
Дата: 09.07.14 10:26
Оценка:
Здравствуйте, Кодт, Вы писали:

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


К>Добавляю #pragma omp parallel for...


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


Не совсем про OMP, но недавно по нужде придумали как убивать числодробительный поток не внося изменений в его код.
Идея — отнять доступ к памяти над которой происходит числодробление.

Используется __try/__except от Микрософта. Но, думаю, можно и портировать на что-нибудь другое.

#include <windows.h>

struct thread_params_t
{
    DWORD  data_numbers;
    DWORD* data_area;
    DWORD  data_area_size;
}; // thread_params_t

void calc_func (thread_params_t& params)
{
    while (1)
    {
        for (DWORD i = 0; i < params. data_numbers; ++ i)
        {
            params. data_area [i] = GetTickCount ();
        }
    }
} // calc_func

DWORD WINAPI calc_thread_func (LPVOID param)
{
    thread_params_t* params = (thread_params_t*) param;

    __try
    {
        calc_func (* params);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }

    return 0;
} // calc_thread_func

DWORD round_up_to_page_size (DWORD size)
{
    SYSTEM_INFO si = { 0 };
    GetSystemInfo (&si);

    return (size + si. dwPageSize - 1) & ~(si. dwPageSize - 1);
} // round_up_to_page_size

int main (int /* argc */, char* /* argv */ [])
{
    thread_params_t thread_params = { 0 };
    thread_params. data_numbers = 10000;
    thread_params. data_area_size = round_up_to_page_size (thread_params. data_numbers * sizeof (DWORD));
    thread_params. data_area = (DWORD*) VirtualAlloc (NULL, thread_params. data_area_size, MEM_COMMIT, PAGE_READWRITE);

    if (thread_params. data_area == NULL)
    {
        return 1;
    }

    DWORD id = 0;
    HANDLE thread = CreateThread (NULL, 0, calc_thread_func, & thread_params, 0, & id);

    Sleep (1000);

    DWORD prev_access = 0;
    VirtualProtect (thread_params. data_area, thread_params. data_area_size, PAGE_NOACCESS, & prev_access);

    WaitForSingleObject (thread, INFINITE);

    CloseHandle (thread);

    VirtualFree (thread_params. data_area, thread_params. data_area_size, MEM_RELEASE);

    return 0;
} // main
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.