C++ Signalhandler

Und jetzt ersetze Z. 50 bis 53. durch ein Aufruf, der möglicherweise hängt. (Z.B. listen(socket) und es kommt keine Verbindung rein.)

Bääm: ^C^C^C^C^C^C^C^C
Versteh mich nicht falsch, dein Ansatz ist eine elegante, saubere, einfache Methode, die aber nicht geht, wenn ein einziger "Abarbeitungsschritt" hängt. Dein Ansatz ist wie das Messagesystem von Windows. Solange alles läuft, kannst du dein Fenster tatsächlich immer schließen, aber sonst "Dieses Fenster reagiert nicht mehr".
 
Moment! Wie schon gesagt war das ein stark vereinfachter Ansatz. Hätte ich das ganze Thread-handling mit reingepackt, wäre die Sache zu groß geworden um's hier zu posten. In der Realität ist es natürlich so, daß man immer damit rechnen muß, daß wenn der Primär-Thread die Sekundär-Threads bittet, sich zu beenden, die Sekundär-Threads nicht immer sofort gleich in der Lage sind, der Bitte Folge zu leisten. Deshalb läßt man den Sekundär-Threads aber auch erst mal eine vernünftige Spanne Zeit, sich sanft zu beenden. Erst wenn sich innerhalb dieses Zeitfensters nichts tut, gibt's via kill TerminateThread() / pthread_cancel() / etc. eins auf die Mütze.

Im Übrigen würde ich in so einem Fall nicht die blockierende Variante von listen() verwenden sondern einfach via select() und mit einem Timeout ... dann kann der Thread auch periodisch mal nachschauen, ob er gebeten wurde, sich zu beenden.
Ergänzung ()

Und was passiert denn in deiner Lösung in solch einem Fall? Dein Signalhandler-Thread räumt einfach völlig ohne Synchronisierung (zumindest so, wie du's gepostet hast) alle registrierten Object-Instanzen weg und zieht den anderen Threads den Boden unterm Arsch weg. Wehe, einer dieser Threads hat dann noch eine Mikrosekunde Zeit, auf die eben zerstörten Objekte zuzugreifen ...
 
Zuletzt bearbeitet:
Klar, im Multithreading geht die ganze Sache nicht gut (man könnte alle anderen Threads anhalten und den Zugriff auf den Singleton serialisieren). Aber wenn du nur die Ports wieder schließen willst, dann ist es doch egal, wenn der zweite Thread dann abstürzt.

Das mit dem listen war natürlich auch gekürzt, aber wenn du beispielsweise über Netzwerk ein Datenstrom empfängst, den du auch noch gleichzeitig verarbeitest und dann dein Programm hängt, weil es auf den Remote-Host wartet, dann geht die Variante mit warten nicht und mit select nur sehr eingeschränkt.
 
Hancock schrieb:
Klar, im Multithreading geht die ganze Sache nicht gut (man könnte alle anderen Threads anhalten und den Zugriff auf den Singleton serialisieren). Aber wenn du nur die Ports wieder schließen willst, dann ist es doch egal, wenn der zweite Thread dann abstürzt.

Hmm, also da wäre ich vorsichtig. Von sauberem Beenden kann da jedenfalls keine Rede mehr sein.

Hancock schrieb:
Das mit dem listen war natürlich auch gekürzt, aber wenn du beispielsweise über Netzwerk ein Datenstrom empfängst, den du auch noch gleichzeitig verarbeitest und dann dein Programm hängt, weil es auf den Remote-Host wartet, dann geht die Variante mit warten nicht und mit select nur sehr eingeschränkt.

Da bin immer noch nicht von überzeugt. :) Auch hier kann man erst per select() fragen, ob denn wirklich Daten verfügbar sind, bevor man sie mit recv() abholt. Es gibt keinen Grund, wieso man da auf den remote host warten müßte.
 
Wenn du per Signal ein Programm sauber beenden willst... :).

Du kannst nur dann select verwenden, wenn du den (Code-)Teil, der liest, überhaupt selber schreiben kannst. Das hab ich damit gemeint.
Dir ist auch klar, dass das mit select ziemlich langsam werden kann? (Auch wenn das hier weniger Relevanz hat.. :))
Statt
Code:
recv(p,c,0);
Code:
int recv(char*p,int c,int){
while(!signaled){
prepare();
if(select(x,y,z,m,nonzero_timeout)){
recv(p++,1,0);
if(!(--c))
return;
}
}
throw exception("Interrupted");
}
 
Mein Senf kurz…

zu zulässigen Funktionen einfach mal *man 7 signal* lesen (printf ist nicht dabei). Diese können ihrerseits natürlich auch wieder durch ein signal unterbrochen werden, es sei denn man setzt die entsprechende signal mask. Checks auf EINTR gehören in den gesamten umliegenden Code natürlich dazu.

Und das wäre der nächste Punkt - signal() ist ein steinaltes und kaputtes API. Stattdessen schön sigaction() nehmen. Und wenn sowieso eine select/poll/epoll message Loop läuft, könnte man auch signalfd() in betracht ziehen. In der manpage zu signalfd ist auch ein hübsches Minimalbeispiel.
Und als letztes noch - signal handler in C++ immer als *extern C* definieren. Rein theoretisch darf C eine andere Callingconvention als C++ benutzen. (unter Tru64, Gott habe es selig, würde einem das um die Ohren fliegen)

Threads sind noch ein ganz eigenes Thema. pthread_sigmask ist quasi Pflicht. Ein Signal wird sonst irgendeinem Thread zugestellt. Hier empfiehlt es sich die existierende Doku extrem ausführlich zu lesen. Fun fact, pthread_cancel wird durch eine C++ exception erledigt ;) Versucht man diese aber mit catch(...) zu fangen (ohne zu rethrowen), gibt's ein abort() um die Ohren geknallt.
 
Zuletzt bearbeitet:
7H3 N4C3R schrieb:
Und als letztes noch - signal handler in C++ immer als *extern C* definieren. Rein theoretisch darf C eine andere Callingconvention als C++ benutzen. (unter Tru64, Gott habe es selig, würde einem das um die Ohren fliegen)

Ich glaube, daß sich das mit C++11 geändert hat. In meinen Projekten sind die Signal-Handler auch noch alle als extern C deklariert, aber diese Referenz -> http://en.cppreference.com/w/cpp/utility/program/signal läßt vermuten, daß das in C++11 nicht mehr notwendig ist.
Ergänzung ()

Meh, nee ... du hast Recht ... ich hätte mal den GANZEN Text lesen sollen. ;)


Notes

Signal handlers are expected to have C linkage and, in general, only use the features from the common subset of C and C++. It is implementation-defined if a function with C++ linkage can be used as a signal handler.
 
Zurück
Oben