problem mit statischen globalen variablen
-
hallo,
ich wuerd mich freuen, falls jemand eine idee zu folgendem problem haette:
wir entwickeln in unserer firma eine c++ bibliothek. innerhalb dieser sind für bestimmte klassen singleton implementierungen vorhanden, wobei das singleton pattern so umgesetzt wurde, dass über eine GetInstance() methode der jeweiligen klasse eine lokale statische variable zurückgeliefert wird, in etwa wie:
Klasse& Klasse::GetInstance() { static instanz; return instanz; }Nun ist es so, dass zusaetzlich zu obigem log4cxx verwendet wird und die logger statische globale variablen sind, in etwa z.B. in modul.cpp:
static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("MyLogger")); Klasse::~Klasse() { Info(logger, "Klasse wird destruiert.."); }geloggt wird hierbei unter anderem auch in den destruktoren der besagten singleton instanzen.
Bei implementierung einer konsolen-anwendung, die die obige bibliothek verwendet, kommt es dazu, dass am ende der anwendung es (zumindest unter dem BSD betriebssystem) zu einer segmentation fault kommt, undzwar genau am ende der applikation.
nun meine frage:
die besagten logger-instanzen sind in der besagten bibliothek an einigen stellen global und statisch (leider alter code und es gibt viele stellen). es scheint so, als ob diese am ende der anwendung destruiert werden, obwohl danach noch andere objekte (die singletons) in ihren destruktoren noch am loggen sind.
kann man hierraus folgern dass man eine andere loesung fuer die statischen globalen logger finden muss, da man ja nicht garantieren kann in welcher reihenfolge diese am ende der anwendung destruiert werden (zuerst die singletons oder die logger) ???vielen dank vorab.
-
teddybaer schrieb:
nun meine frage:
(...)
kann man hierraus folgern dass man eine andere loesung fuer die statischen globalen logger finden muss, da man ja nicht garantieren kann in welcher reihenfolge diese am ende der anwendung destruiert werden (zuerst die singletons oder die logger) ???Jain.
Man kann soweit ich weiss die Reihenfolge schon garantieren, aber halt nicht garantieren dass man eine "passende" Reihenfolge bekommt.
Wenn ich mich nicht irre werden function-local-statics (das was ihr verwendet) in der umgekehrten Reihenfolge zerstört wie sie konstruiert wurden.
(OK, sieht so aus als ob ich mich hier richtig erinnert habe: http://stackoverflow.com/questions/469597/destruction-order-of-static-objects-in-c )Dummerweise ist das aber die falsche Reihenfolge, wenn z.B. in einem Konstruktor eines statischen Objekts das erste mal auf einen Logger zugegriffen wird.
Der Konstruktor des statischen Objekts wurd dann ja als erster aufgerufen, und dann erst der Konstruktor des Loggers. Also wird - umgekehrte Reihenfolge - der Logger zuerst zerstört => BOOM.
(Wobei ich mir hier wieder nicht sicher bin - nämlich welcher Zeitpunkt für das "reverse order" Zerstören als Konstuktionszeitpunkt gilt -- der wo der Konstruktor des Objekts anfängt oder der wo er beendet ist. Wenn zweiteres, dann würde die Reihenfolge in dem Fall sogar passen.)EDIT: Ich hab das mal kurz ausprobiert... der aktuell von ideone.com verwendete Compiler nimmt den Zeitpunkt wo die Konstuktion abgeschlossen wurde: http://ideone.com/rnEqaT
Das wäre in diesem Fall ja die passende Reihenfolge.
Ob der Standard das so vorschreibt weiss ich nicht. /EDIT----
Das ganze ist ein altes Problem, und du wirst viele Artikel zu dem Thema finden wenn du mit "static initialization order fiasco" bzw. "static destruction order fiasco" suchst.
Ein paar (mehr oder weniger) übliche Lösungen die mir auf die Schnelle einfallen:
* Logger per "new" anlegen und nie zerstören.
* Logger nicht direkt global machen, sondern nur
shared_ptr<Logger>als globale Variable. Alle Objekte die einen bestimmten Logger verwenden wollen müssen dann einenshared_ptr<Logger>auf diesen Logger halten, und über diesen zugreifen. Dadurch wird der Logger erst zerstört nachdem alle "User" des Loggers zerstört wurden.* Alle nicht-Logger Singletons entfernen.
* Überhaupt alle Singletons entfernen.
* Alle nicht-Logger Singletons durch eine Singleton Implementierung ersetzen die eine "zerstör es jetzt" Funktion hat, die man dann vor Beendigung des Programms aufruft.
-
ja, so etwas in der art hatte ich schon befuerchtet.
vielen dank fuer die umfangreiche antwort.
-
Falls das oben erwähnte Verhalten standard ist, und auch von eurem Compiler so umgesetzt wird, dann sollte es vorerst mal reichen, in allen Klassen wo der Destruktor loggt, auch im Konstruktor auf den (selben) Logger zuzugreifen.
Dazu muss man im Konstruktor nichtmal loggen, ein einfaches "get logger" wäre ausreichend.
(D.h. ausser die Logging-Lib verwendet intern noch weitere local statics, z.B. für Log-Writer, die erst angelegt werden wenn man wirklich was loggt. In dem Fall müsste man dann im Konstruktor wirklich ne Log-Ausgabe machen.)
-
ich glaube wir werden eher die shared_ptr variante fahren.
aber das mit deinem kommentar zum einfuegen einer logg-anweisung
in den konstruktor der singletons verstehe ich nicht so recht:[code]
SingletonConstructor()
{
Info(logger, "log was");
}
[\code]du meinst es waere ggf. moeglich dass hier die konstruktionsreihenfolge
1. logger
2. Singleton
gelten koennte und demnach die destruktion:
1. Singleton
2. logger
folgt ?
-
noch eine anmerkung:
das beispiel auf http://ideone.com/rnEqaT
verwendet fuer beide objekte lokale statische variablen.in meinem beispiel waere aber eine der beiden variablen
eine globale statische variable.ich hoffe das macht keinen unterschied ?
-
Oh ich bin so doof.
Ich hab übersehen dassLoggerPtrja dem Namen nach vermutlich bereits ein Zeiger ist. Und vermutlich (hoffentlich) einer mit Reference-Counting.Also brauchst du keine
shared_ptr, kopier einfach denLoggerPtralsLoggerPtrin das Objekt => Problem gelöst.teddybaer schrieb:
noch eine anmerkung:
das beispiel auf http://ideone.com/rnEqaT
verwendet fuer beide objekte lokale statische variablen.in meinem beispiel waere aber eine der beiden variablen
eine globale statische variable.ich hoffe das macht keinen unterschied ?
Doch, das würde einen Unterschied machen. Hab ich übersehen. Aber wie gesagt: kopier einfach den
LoggerPtr.