Необработанное принудительное разматывание вызывает прерывание - PullRequest
3 голосов
/ 22 января 2011

Таким образом, я понимаю, что и pthread_exit, и pthread_cancel таковы, что оба они приводят к тому, что вещь, похожая на исключение, называемая "принудительное разматывание", выбрасывается из соответствующего стекового кадра в целевом потоке.Это может быть перехвачено, чтобы выполнить специфичную для потока очистку, но должно быть повторно выброшено, иначе мы получим неявное значение abort() в конце блока catch, который не перебрасывал.

В случае pthread_cancel это происходит либо сразу после получения соответствующего сигнала, либо следующего входа в точку отмены, либо когда сигнал разблокируется в следующий раз, в зависимости от состояния и типа отмены потока.

В случае pthread_exit вызывающий поток немедленно подвергается принудительному раскручиванию.

Fine.Это «исключение» является нормальной частью процесса уничтожения потока.Так почему же, даже когда я перебрасываю его, вызывается std::terminate(), который прерывает работу всего моего приложения?

Обратите внимание, что я перехватываю и перебрасываю исключение пару раз.

Обратите внимание, что я звоню pthread_exit из моего SIGTERM обработчика сигнала.Это прекрасно работает в моем игрушечном тестовом коде, скомпилированном с g ++ 4.3.2, в котором поток запускается signal(SIGTERM, handler_that_calls_pthread_exit), а затем находится в тесном цикле while, пока не получит сигнал TERM.Но это не работает в реальном приложении.

Соответствующие кадры стека:

(gdb) where
#0  0x0000003425c30265 in raise () from /lib64/libc.so.6
#1  0x0000003425c31d10 in abort () from /lib64/libc.so.6
#2  0x00000000012b7740 in sv_bsd_terminate () at exception_handlers.cpp:38
#3  0x00002aef65983aa6 in __cxxabiv1::__terminate (handler=0x518)
    at /view/ken_gcc_4.3/vobs/Compiler/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:43
#4  0x00002aef65983ad3 in std::terminate ()
    at /view/ken_gcc_4.3/vobs/Compiler/gcc/libstdc++-v3/libsupc++/eh_terminate.cc:53
#5  0x00002aef65983a5a in __cxxabiv1::__gxx_personality_v0 (
    version=<value optimized out>, actions=<value optimized out>, 
    exception_class=<value optimized out>, ue_header=0x645bcd80, 
    context=0x645bb940)
    at /view/ken_gcc_4.3/vobs/Compiler/gcc/libstdc++-v3/libsupc++/eh_personality.cc:657
#6  0x00002aef6524d68c in _Unwind_ForcedUnwind_Phase2 (exc=0x645bcd80, 
    context=0x645bb940)
    at /view/ken_gcc_4.3/vobs/Compiler/gcc/libgcc/../gcc/unwind.inc:180
#7  0x00002aef6524d723 in _Unwind_ForcedUnwind (exc=0x645bcd80, 
    stop=<value optimized out>, stop_argument=0x645bc1a0)
    at /view/ken_gcc_4.3/vobs/Compiler/gcc/libgcc/../gcc/unwind.inc:212
#8  0x000000342640cf80 in __pthread_unwind () from /lib64/libpthread.so.0
#9  0x00000034264077a5 in pthread_exit () from /lib64/libpthread.so.0
#10 0x0000000000f0d959 in threadHandleTerm (sig=<value optimized out>)
    at osiThreadLauncherLinux.cpp:46
#11 <signal handler called>

Спасибо!

Эрик

1 Ответ

4 голосов
/ 29 июня 2011

Обратите внимание, что я звоню pthread_exit из моего сигнала SIGTERM обработчик.

Это твоя проблема. Цитировать из спецификации POSIX (http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html):

Если сигнал возникает не в результате вызова abort (), повышение (), kill (), pthread_kill () или sigqueue (), поведение не определено , если обработчик сигнала ссылается любому объекту со статической продолжительностью хранения, кроме как путем присвоения значения объекту, объявленному как volatile sig_atomic_t, или , если обработчик сигнала вызывает любую функцию в стандартной библиотеке, кроме одной из функций, перечисленных в Концепции сигнала.

Список разрешенных функций приведен в http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03, и не включает pthread_exit(). Поэтому ваша программа демонстрирует неопределенное поведение.

Я могу придумать три варианта:

  1. Установите флаг в обработчике сигналов, который периодически проверяется потоком, вместо того, чтобы пытаться выйти непосредственно из обработчика сигналов.
  2. Используйте sigwait() для явного ожидания сигнала в независимом потоке. Затем этот поток может явно вызвать pthread_cancel() в потоке, который вы хотите выйти.
  3. Маскируйте сигнал и периодически вызывайте sigpending() в потоке, который должен быть завершен, и завершайте работу, если сигнал находится в состоянии ожидания.
...