std::map eines Singleton in Library plötzlich leer



  • Hallo zusammen,

    ich möchte mir ein Apache-Modul ala JSP für C++ schreiben.
    Generell funktioniert das auch schon ganz gut, allerdings habe ich das Problem, dass mir meine map in einem Singleton, in der ich meine erstellten Servlets verwalte, plötzlich leer ist, wenn ich die Datei editiere, die der Apache mit der URI assoziiert. Es passiert beim ersten Editieren (glaub ich) immer und später nur gelegentlich.

    Ich versteh es absolut nicht... Es wird nur eine Instanz dieses Singleton erstellt (habe im Konstruktor etwas in eine Datei appended). Die Adresse bleibt auch die gleiche. Ich hab auch schon ne Zufallszahl in das Singleton gemacht, die auch immer identisch ist. Also kann die Library (.so-Datei) durch den Apache ja auch nicht ein zweites mal geladen werden.

    So dann mal etwas Code:
    Also xsp_handler ist der Einstiegspunkt für den Apache:

    class Foo{
    private:
            Foo(){}
            Foo(const Foo&) = delete;
            Foo& operator=(const Foo&) = delete;
            static Foo _instance;
    
            std::map<std::string, void*> _map; // in der Praxis antürlich kein void*
    
    public:
            static Foo& instance(){
                    return _instance;
            }
    
            void handle(request_rec& r){
                    if(_map.empty())
                            ap_rprintf(&r, "empty");
                    else
                            ap_rprintf(&r, "not empty");
                    _map.insert(std::make_pair(r.uri, static_cast<void*>(0)));
                    if(_map.empty())
                            ap_rprintf(&r, " - empty");
                    else
                            ap_rprintf(&r, " - not empty");
    
            }
    };
    
    Foo Foo::_instance;
    
    static int xsp_handler(request_rec *r){
            Foo& foo(Foo::instance());
            foo.handle(*r);
            return 0;
    }
    

    Bei ersten Aufruf ist der Output:

    empty - not empty

    solange ich die Datei nicht editiere bleibt er danach bei:

    not empty - not empty

    Nach dem ersten Editieren wieder:

    empty - not empty

    Bei weiterem Editieren meistens

    not empty - not empty

    doch gelegentlich

    empty - not empty

    Also in meiner komplexen Version habe ich die map natürlich synchronisiert,
    aber dort tritt es auch auf.

    Was ich besonders irritierend finde:
    In Wirklichkeit ist in meiner map ein shared_ptr, der eine Servlet beinhaltet, in dem eine Library ist, die aus der "xsp"-Datei erzeugt und kompiliert wurde.
    Damit ich die Library beim Aktualisieren der Datei nicht einer anderen Anfrage unterm Hintern wegziehe häng ich einen Timestamp an den Dateinamen. Im Destruktor des Servlets wird die dazugehörige Datei gelöscht. Die shared_ptr der Servlets habe ich (außer in der map) nur auf dem Stack.
    Es wird dann immer bei der Ausgabe "not empty - empty" eine neue Datei erzeugt. Die alte Datei wird nicht gelöscht. Beim Herunterfahren des Apache werden dann alle gelöscht.
    Generell würde ich sagen ein klarer Fall von mehreren Instanzen, aber das Singleton hat immer die gleiche Adresse, die gleiche Random-Number, wenn ich eine im Konstruktor erzeuge, es wird nur einmal etwas in eine Datei geschrieben, wenn ich im Konstruktor etwas appende. Ich versteh die Welt langsam nicht mehr... Wenn der Apache irgendwelche Byte-Blöcke einfach kopieren sollte (, was ich mir nicht vorstellen kann), dann müsste ich für meine map vermutlich ein double delete kriegen und Einträge sollte ja wohl auch nicht verschwinden (oder die map ist völlig kaputt). Zumindest würden dann die Destruktoren nicht mehr alle aufgerufen.

    Hat irgendjemand evtl. einen Ratschlag für mich, woran es liegen könnte oder was da abläuft?

    Gruß,
    XSpille

    EDIT: not empty vs. empty Dreher



  • Wieso experimentierst du mit einer Zufallszahl rum? Wirf doch statt dessen einfach mal ein Trace/printf/... in den Konstruktor und Destruktor von Foo.

    Was das "Adresse bleibt gleich" angeht: unter Windows ist das ganz normal. Wenn du eine DLL entlädst und dann wieder neu lädst, dann stehen die Chancen sehr sehr gut dass die Adresse sämtlicher statischen Teile gleich bleibt.
    Es ist nur dann nicht so, wenn die bevorzugte Basisadresse der DLL nicht frei ist, und für's Rebasen der DLL eine andere Adresse ausgesucht wird wie beim letzten mal als die DLL geladen wurde (wie häufig das vorkommt weiss ich nicht, aber wenn man sich eine eigene bevorzugte Basisadresse aussucht, und nicht einfach den Default-Wert übernimmt, dann wird normalerweise nicht rebased, d.h. man hat auch kein Problem).

    EDIT:

    Es wird nur eine Instanz dieses Singleton erstellt (habe im Konstruktor etwas in eine Datei appended).

    Sorry, das hatte ich überlesen 🙂
    Tjoah, das ist dann wirklich halbwegs mysteriös. 😕



  • Leider ist dein Post etwas unvollständig. Tritt der Fehler auch in diesem Beispiel auf, oder hast du den Code für die Lesbarkeit reduziert ?

    1. Die Deklaration einer statischen Variable in einem Header kann ganz ungeahnte Auswirkungen haben, da die statische Variable in jeder Compile-Unit vorhanden ist, die den Header includiert.
    Daher mal in der Linux-Shell mal nm auf die fertigen SharedObjects machen.

    2. Wie viele verschiedene Apache-Prozesse laufen denn in deinem Beispiel? Jeder Apache-Prozess lädt die Library nochmals, so dass der Inhalt von _instance auch unterschiedlich sein kann. Die (relative) Speicher-Adresse bleibt natürlich gleich (außer man hat gewisse Schutzmechanismen aktiviert).
    Auch falls es dir selbst klar ist, schreibe ich es mal: Ein Singleton ist kein Prozess-Übergreifender Shared-Memory.

    3. Falls ein Apache-Prozess mal abraucht (SEGFAULT), kriegst du das ggf. nicht mit, so dass die Map im Nachhinein leer erscheint.


Log in to reply