malloc und placement new



  • Hiho,

    Ich soll ein Apache Modul schreiben, und hab da eine kleine Frage bezüglich Speicher reservieren. Wie die meisten ja sicher wissen macht man das in C; das Memory Management wurde einer eigenen API implementiert, in dem Speicher in verschiedenen Pools (Request Pool, Process Pool,....) alloziert werden kann, und Apache kümmert sich dann anschließend um das Aufräumen (wenn der Request fertig abgearbeitet wird, wird der Speicher den ich im Request Pool angefordert hab freigegeben etc..). Soweit so schön.

    Ich will allerdings nette Features der STL wie z.B. std::string oder std::map verwenden bzw. eben meine eigenen Objekte im Speicher ablegen.
    Wenn ich ein Objekt in einen Pool legen möchte, muss ich die C-API (die vermutlich im Hintergrund eben ein malloc macht) Speicher reservieren. Und hier eben mein eigentliches Problem: wie mach ich das am besten? Das ist ja im Prinzip nicht ganz so toll...

    Ich hab ein bisschen herumgegraben, und bin auf "placement new" gestoßen (http://www.parashift.com/c++-faq-lite/placement-new.html).

    Wenn ich also Objekte in einen Speicherpool legen möchte würde das folgendermaßen aussehen:

    void *p = NULL;
    MyObject *o = NULL;
    *p = apr_pcalloc(memory_pool, sizeof(MyObject)); //Speicher in einem Apache Pool reservieren
    o = new(p) MyObject(); //eigentliches Objekt instantiieren
    

    Kopfzerbrechen macht mir allerdings folgendes Statement:

    Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your class needs to be aligned on a 4 byte boundary but you supplied a location that isn't properly aligned, you can have a serious disaster on your hands (if you don't know what "alignment" means, please don't use the placement new syntax).

    Nun muss ich zugeben, dass meine C/C++ Kenntnisse schon ziemlich eingerostet sind und ich zwar eine Idee habe was "alignment" ist, mir aber nicht sicher bin. Point being: ich bin mir nicht sicher ob es mir Probleme bereiten wird, darum bin ich obiger Variante eher abgeneigt.

    Danach bin ich noch auf folgendes gestoßen:
    http://zach.chambana.net/apache-cplusplus/cplusplus/memory.html
    Im Endeffekt hat er auf das ganze Memory Management von Apache verzichtet und sich selbst um das Aufräumen gekümmert.

    Folgende Möglichkeiten fallen mir also ein:

    1.) alles in C schreiben
    2.) placement new verwenden
    3.) Apache Memory Management nicht verwenden

    Momentan bin ich eher der Meinung Variante 3 zu verwenden. Oder ist "placement new" doch nicht so Tragisch wie ich es mir gerade ausmale?

    TIA,

    cheers
    Christian



  • Kapsel erstmal alles in einen Allocator, dann ist es automatisch einfacher. 😉
    http://www.cplusplus.com/reference/std/memory/allocator/

    malloc wird immer Speicherbereiche mit korrekten Addressen zurückgeben, die selbe Problematik hat man in C ja auch. In der Regel ist new auch intern über malloc implementiert.

    Problematisch wäre zb:

    void* buffer = malloc(sizeof(std::string) + 1);
    buffer++; // Jaja, Cast nach char* denken.
    new (buffer) std::string();
    

    Genug Speicher für std::string wäre da, aber die Addresse ist "schief".



  • Wie Ethon schon sagte, wirst du einen Allokator dazu schreiben muessen. Damit ist es aber nicht getan. Du musst statt std::string eben std::basic_string<char, std::char_traits<char>, your_allocator<char>> verwenden, und statt std::map<K, V> std::map<K, V, std::less<K>, your_allocator<std::pair<K const, V>>. Selbes gilt natuerlich auch fuer andere STL-Container.



  • Kapsel erstmal alles in einen Allocator, dann ist es automatisch einfacher.

    Ja sowieso, das sind ja im Prinzip immer dieselben Zeilen Code 🙂

    Ich hab jetzt nicht unbedingt vor Pointerarithmetik zu verwenden bzw. wüsste jetzt momentan nicht wo ich das brauche hier. aber ich behalte es natürlich im Hinterkopf 😉

    thx,

    Christian



  • Wenn ich es mir recht überlege muss ich eigentlich nur Allocator für meine eigenen Objekte schreiben, da eigentlich nur diese im (permanenten) Prozesspool liegen werden.

    Für die Methoden die in den Requests aufgerufen werden und deren Variablen im Requestpool werde ich mich glaub ich selbst darum kümmern dass aufgeräumt wird sofern ich da Pointer benötige.

    cheers
    Christian



  • Christian Erlinger schrieb:

    Wenn ich es mir recht überlege muss ich eigentlich nur Allocator für meine eigenen Objekte schreiben, da eigentlich nur diese im (permanenten) Prozesspool liegen werden.

    Den Allocator schreibst du doch eh als Template, also ist es egal für welchen Typ der verwendet wird. 😉

    Ansonsten achte darauf, dass du im Speicher, der von Apache verwaltet wird, keine Objekte mit nicht-trivialem Destruktor anlegst, der wird nämlich dann auch nicht augerufen. (Außer du rufst ihn händisch auf, was ungut ist)



  • Zum Thema Alignment: malloc liefert immer eine Adresse, die für alles richtig "aligned" ist. Man sollte davon ausgehen können, dass apr_palloc sich auch daran hält.

    Was mir ein bisschen gefährlich erscheint, ist das Erzeugen von non-PODs in diesen Pools; denn non-PODs wollen sicherlich "ordentlich" zerstört werden. Das heißt: Es wird erwartet, dass der Destruktor aufgerufen wird, bevor der Speicher freigegeben wird. Du musst also dafür sorgen, dass die Destruktoren von non-PODs immer vor der Freigabe des Poolspeichers aufgerufen werden. Das könnte man vielleicht auch automatisch erreichen, wenn man apr_pool_cleanup_register verwendet. Musst du selbst mal überprüfen. Allerdings muss man dann ein bisschen auf Doppel-Löschungen aufpassen, wie es aussieht. Vielleicht kann man sich da auch einen netten neuen schlauen Zeiger selbst bauen, der einem das apr_pool_cleanup_register und ggf ein apr_pool_cleanup_kill abnimmt. Das muss man sich auch mal zu Ende überlegen...

    Also, rein aus Performancegründen (Speicherfragmentierung) könnte die Nutzung dieser Pools schon sinnvoll sein...



  • apr_pool_cleanup_register scheint mir zumindest schon gleich mal behilflich zu sein dass meine Objekte die ich mit new angelegt habe im Zuge des Pool freigeben auch wieder "automatisch" freigegeben werden:

    http://www.apachetutor.org/dev/pools.annot?when=5&sect=6

    Wenn ich mal soweit bin werd ich meinen Ansatz posten, ansonsten danke für die Tipps schon mal 🙂

    cheers
    Christian


Log in to reply