2016-06-07 5 views
8

sich das folgende Programm:C++: Beenden ohne aktive Ausnahme (GCC) genannt

#include <iostream> 
#include <pthread.h> 
#include <stdexcept> 
#include <unistd.h> 

static void* busy(void*) 
{ 
    int oldstate ; 
    auto result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldstate) ; 
    if (result != 0) 
#ifdef NOEXCEPT 
    { std::cerr << "pthread_setcanceltype" << std::endl ; abort() ; } 
#else 
    throw std::runtime_error("pthread_setcanceltype") ; 
#endif 
    while (true) 
    ; 
    return nullptr ; 
} 

static pthread_t start() 
{ 
    pthread_t t ; 
    int result = pthread_create(&t,nullptr,busy,nullptr) ; 
    if (result != 0) 
    throw std::runtime_error("pthread_create") ; 
    return t ; 
} 

static void terminate(pthread_t t) 
{ 
    auto result = pthread_cancel(t) ; 
    if (result != 0) 
    throw std::runtime_error("pthread_cancel()") ; 
    result = pthread_join(t,nullptr) ; 
    if (result != 0) 
    throw std::runtime_error("pthread_join()") ; 
} 

int main() 
{ 
    auto t = start() ; 
    sleep(1) ; // may not cause an abort otherwise 
    terminate(t) ; 
    return 0 ; 
} 

Dies läuft gut, solange keine Optimierung (oder -O1) verwendet wird, z.B. mit g ++ -std = C++ 11 -Wall -o Test test.cc -thread

Mit -O2 oder -O3 bricht das Programm jedoch mit der obigen Meldung ab.

Auch irgendwie interessant: es läuft durch, wenn mit -DNOEXCEPT kompiliert. So scheint es, wenn ein Thread in einer Funktion abgebrochen wird, die möglicherweise [sic!] Eine Ausnahme auslöst, und wenn die Optimierung eingeschaltet ist, kann das Programm abbrechen. - Und ich kann keinen Weg sehen, das zu verhindern.

Es ist für mich reproduzierbar auf amd64 gcc 4.8.4 (Ubuntu 14.04.3) und armv7l gcc 4.9.2 (Raspbian 4.9.2-10).

Können Sie das reproduzieren? Hast du eine Erklärung? Dieses Verhalten scheint (zumindest für mich) seltsam zu sein. Ich würde mich über Feedback freuen. Vielen Dank!

+5

Ich glaube, der Ort, den Sie erwähnen (bitte Link, wenn sie etwas auf dem World Wide Web beziehen) sprach über 'std :: thread' und nicht über pThreads. – molbdnilo

+1

Ein anderer [nützlicher Artikel] (https://skaark.wordpress.com/2010/08/26/pthread_cancel-condensed-harmful/). Obwohl es nicht genau dein Problem ist, skizziert es andere Probleme mit 'pthread_cancel'. Bearbeiten: ein anderes [ähnliches Problem] (https://gcc.gnu.org/ml/gcc/2007-06/msg00020.html) –

+0

Sie verwenden Pthreads, nicht C++ 11 Threads. In C++ 11-Threads gibt es keine Thread-Löschung. Wie nackte Pthreads mit C++ - Ausnahmen oder integrierten C++ 11-Thread-Funktionen interagieren (die wahrscheinlich noch pthreads auf Ihrer Plattform sind, aber in eine Art C++ - fähigen Wrapper eingebettet sind), ist eine Vermutung. –

Antwort

2

Unter Linux (wie auf den meisten Betriebssystemen) Ausnahmen ein sprachunabhängig Feature sind, und pthread Stornierung mit sprachunabhängig Ausnahmen umgesetzt (siehe zum Beispiel Cancellation and C++ Exceptions). Wenn eine PThread-Annullierung an einen Thread übergeben wird (mit einem Signal, aber Sie müssen das nicht wissen), ruft die Abwicklungs-Maschinerie alle installierten Persönlichkeiten auf, so dass sie eine sprachspezifische Bereinigung durchführen können, bevor der Thread beendet wird . (Dies ist ziemlich cool; es bedeutet, dass wie im obigen Artikel können Sie einen Fangblock für abi::__forced_unwind einfügen, um zu erkennen - obwohl nicht zu verhindern - eine Thread-Stornierung.)

Das Problem ist, dass eine asynchrone Auslöschung bei jedem auftreten kann Instruktionen und die von g ++ generierten C++ - Ausnahmetabellen behandeln nur Ausnahmen, die bei Instruktionen auftreten, von denen bekannt ist, dass sie Exceptions erzeugen können (dh, aber nicht nur Aufrufe von Exception-throwing-Funktionen). Wenn eine Ausnahme an einem Punkt generiert wird, der nicht von den C++ - Tabellen abgedeckt wird, wird die C++ - Eigenschaft in Panik versetzt und beendet den Prozess (daher "terminate called ohne eine aktive Ausnahme").

Der Grund, dass dies von der Optimierung beeinflusst wird, ist, dass die C++ - Persönlichkeit lazy installiert ist, aber mit höheren Optimierungsstufen könnte der Compiler präventiv die C++ - Persönlichkeit installieren. Sie können den Absturz auch bei niedrigeren Optimierungsstufen durch Ausführen der C++ - Ausnahme-Maschinerie, z.B. mit try { throw 0; } catch (int) {}.

Die einfachste Lösung besteht darin, sicherzustellen, dass die C++ - Kennung nicht in dem Thread installiert ist, den Sie asynchron abbrechen möchten. Sie können dies sicherstellen, indem Sie die Thread-Funktion als C kompilieren und keine C++ - Funktionen davon aufrufen.

Ein hacky und hoch ungestützte Lösung besteht darin, sicherzustellen, dass alle asynchronen Stornierungspunkte (das heißt, alle Befehle in dem das annullierte Gewinde sein könnte, wenn die asynchrone Löschung empfangen wird) sind in der Tat durch die abgedeckte C++ - Tabellen werden entfernt. Zuerst müssen Sie mit -fnon-call-exceptions kompilieren; Zweitens müssen Sie sicherstellen, dass jeder Befehl, der ein asynchroner Löschpunkt sein könnte, zwischen zwei Punkten ist, von denen bekannt ist, dass sie synchrone Löschpunkte sind, z. pthread_testcancel:

static void* busy(void*) 
{ 
    int oldstate ; 
    auto result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldstate) ; 
    if (result != 0) 
#ifdef NOEXCEPT 
    { std::cerr << "pthread_setcanceltype" << std::endl ; abort() ; } 
#else 
    throw std::runtime_error("pthread_setcanceltype") ; 
#endif 
    pthread_testcancel(); 
    for (unsigned i = 1; ; ++i) 
    if (i == 0) 
     pthread_testcancel(); 
    return nullptr ; 
} 
+0

Vielen Dank für die Einsicht! :-) Wenn ich es richtig verstanden habe, behandelt die C++ Exception-Behandlung Pthreads. Es wird sogar empfohlen, dass Pthread-Löschung zusätzlich zu C++ - Ausnahmen implementiert werden sollte (https://groups.google.com/forum/#!topic/comp.programming.threads/ZSvPGG-79pc). Um es jedoch klar zu sagen, es funktioniert nicht immer mit GCC (da die Implementierung der Ausnahmetabelle nicht alle Stornopunkte umfasst). Der sichere Weg besteht also entweder darin, die asynchrone Löschung abzubrechen oder auf Ebene C zurückzugreifen. Eine Umgehung kann sein, zusätzliche Löschpunkte einzuführen (wie in Ihrem Beispiel). – mikro77

-1

Jemand schrieb hier, dass ein Programm abbricht "wenn ein Thread-Objekt den Gültigkeitsbereich verlässt und sich im verknüpfbaren Status befindet".

Das ist, was 39.3.1.3/1 [thread destructor] sagt tatsächlich:

Wenn joinable() ruft std :: terminate(). [...] Daher muss der Programmierer sicherstellen, dass der Destruktor niemals ausgeführt wird, solange der Thread noch verbindbar ist.

+0

Der verwendete Code verwendet 'pthread', nicht' std :: thread'. –

+0

Irrelevant für die Frage insgesamt. – SergeyA

+0

Nun, eigentlich habe ich auf den ersten Satz geantwortet. Der Rest der Frage basiert auf einer falschen Annahme und behandelt die (sagen wir mal) _wrong_ Threads, also ... ist er mit "C++ 11" markiert und Threads sind Teil der Standard Template Library seit C++ 11 als Soweit ich weiß. – skypjack