SucheBlog abonnierenVerwaltung des BlogsKategorienKontaktMarkus Brückner |
Mittwoch, 21. Dezember 2005A-Gen 0.1.2 released
Vorhin eben habe ich mal wieder ein paar kleinere Probleme an A-Gen behoben und eine neue Version veröffentlicht. Jetzt sollte das Package wieder unter Debian unstable und Ubuntu installieren und der Code mit GCC 4.x kompilieren. Damit ist Version 0.1 soweit am Ende ihrer Entwicklung. Welche Features Version 0.2 genau bieten wird, weiß ich noch nicht. Auf jeden Fall soll die Möglichkeit zum automatischen Aufsplitten der Index-Seite kommen.
Sonntag, 18. Dezember 2005Template-Spielereien die x-teDie schon vor längerer Zeit mal vorgestellten Templatespielereien sind irgendwann tatsächlich mal in ein paar Projekten benutzt worden. Aus verschiedenen Gründen kam plötzlich die Anforderung: "Mach den Logger doch mal bitte thread-safe!". Äh, ja... Konkret sollte der Ausdruck logger << LEVEL_DEBUG << "Eine Debug-Nachricht." << endl; nicht unterbrechbar sein, damit sich in der Ausgabe keine Nachrichten überlappen. Das ist nicht ganz so trivial, wie es scheint, denn in Wirklichkeit ist das nicht ein einziger Ausdruck, sondern dort stehen insgesamt 3 mehr oder minder unabhängige Teilausdrücke. Formt man dieses Zeile in die klassische Aufrufform um, so sieht das so aus (mal die Version der freien Operatoren, die nicht Element eines Typs sind, verwendet): operator<<(operator<<(operator<<(logger, LEVEL_DEBUG), "Eine Debug-Nachricht."), endl);. Man sieht also, daß hier dreimal ein operator<< aufgerufen wird, dessen Rückgabewert als Parameter für weitere Aufrufe verwendet wird. Um nun die gesamte Zeile thread-safe zu bekommen, muss der innerste Aufruf (welcher zuerst ausgeführt wird) einen Mutex locken und der äußerste muss ihn freigeben. Ersteres ist einfach: da das erste Element einer Zeile immer ein Level sein soll, kann man das locken des Mutex in diesen verlagern. Die zweite Anforderung ist schon schwieriger. Es ist für einen Operator nicht feststellbar, daß er der äußerste (und damit letzte einer Zeile) ist. Nun könnte man festlegen, daß jede Zeile mit endl angeschlossen werden muss und dann bei Empfang dieses Modifiers den Mutex wieder freigeben. Aus demselben Grund, wieso ein Operator nicht feststellen kann, daß er der letzte ist, kann man allerdings die Regel mit dem endl nicht erzwingen. Es ist zwar möglich, zu verhindern, daß auf ein endl noch etwas folgt, allerdings kann nicht erzwungen werden, daß überhaupt eines auftauchen muss. Um nun zu vermeiden, daß sich der Programmierer um diese internen Details kümmern muss, kann man die Lebenszeitregeln von C++-Objekten nutzen. Dazu modifiziert man die Ausgabeoperatoren so, daß der Typ von logger direkt nur noch Loglevel-Typen als Parameter akzeptiert. So erzwingt man, daß das erste einer Zeile ein Level ist. Der Ausgabeoperator verpackt den übergebenen Logger dann in ein temporäres Objekt und liefert dieses zurück. Außerdem lockt er den Mutex. Die folgenden Ausgabeoperatoren nehmen als ersten Parameter dieses temporäre Objekt, führen ihre Ausgabeoperation auf den enthaltenen Logger aus und liefern das Objekt wieder zurück. Auch der letzte Operator in der Zeile liefert dieses Objekt zurück. Da es von niemandem mehr entgegen genommen wird, wird es vernichtet. Dabei wird sein Destruktor aufgerufen, welcher den gelockten Mutex wieder freigibt. Auf diese Weise ist die gesamte Zeile vor Unterbrechung geschützt. Um den Logger nun portabel zu halten, sollte das schützende Mutex-Objekt nicht Element des Loggers sein. Leider ist Multithreading meist mit der Verwendung systemspezifischer Bibliotheken verbunden, so daß der Logger dadurch auf ein bestimmtes System festgelegt würde. Um das zu vermeiden, verwendet man ein Proxy-Objekt, welches eine Schnittstelle Lock() und Unlock() anbietet. Dieses nutzerdefinierte Objekt kapselt den eigentlichen, systemspezifischen Mutex. Dieses Objekt (bzw. genaugenommen dessen Typ) wird dem Logger in Form eines Templateparameters übergeben. Dieser ruft die entsprechenden Funktionen dann bei Bedarf auf. Auf diese Weise bleibt der eigentliche Logger-Code unabhängig vom zugrundeliegenden Threadframework. Diese skizzierten Ansätze habe ich im ursprünglichen Logger implementiert, den Code noch etwas aufgeräumt und auf einer eigenen Seite (Vorsicht: Englisch) abgelegt. Dort findet sich auch die Schnittstellendokumentation und etwas übersichtlichere Hinweise zur Benutzung.
(Seite 1 von 1, insgesamt 2 Einträge)
|