problem mit shared_ptr, containern und zeitpunkt der objekt zerstörung



  • CStoll schrieb:

    Der Container ruft auch die Desturoktoren der Elemente auf, wenn er zerstört wird (sprich: letztendlich vermutlich bei Programmende). Ansonsten kannst du mit map::erase() auch einzelne Elemente löschen (das dürfte im Zusammenhang mit shared_pointer auch die File-Handles freigeben), die nicht mehr benötigt werden.

    das hilft mir nicht ;). dass der container seine elemente selbst zerstört, ist mir bekannt(habs nur idiotischerweise als "nie" geschrieben,weil ich "nie im laufenden programm" meinte), aber das ist zu spät. sie müssen dann zerstört werden, wenn sie nicht mehr gebraucht werden. Dass erase benutzt werden muss, um einzelne elemente zu löschen, ist mir ebenso bekannt(siehe die gepostete destructor funktion). Das problem liegt daran, dass ich mit shared_ptr zwar sicher den moment bestimmen kann, wann FileHandle zerstört werden muss(und zwar, wenns niemand mehr braucht), aber nicht, wann die elemente im container zerstört werden. immerhin könnte der container schon längst kaputt sein.



  • Wenn ich das shared_ptr Konzept richtig im Kopf habe, wird das gepointerte Objekt gelöscht, wenn der letzte darauf verweisende shared_ptr sich verabschiedet. Und die map speichert eine Kopie des übergebenen Pointers (btw sollte der nicht mit "handles.insert(make_pair(filePath,newHandle));" eingetragen werden?), d.h. nach deinem Aufruf hast du (mindestens) zwei Pointer auf dein Handle (#1 vom Vector aus, #2 hast du gerade ans Hauptprogramm weitergereicht, weitere können durch Zuweisungen etc. im Hauptprogramm entstehen) - sobald der letzte davon gelöscht wird, löscht sich auch der dahinterliegende Handle.



  • Du hast nicht geschrieben, was die Klasse FileHandle macht. Wenn sie den Zugriff auf das File zur Verfügung stellt, könntest Du das an eine eigentliche Klasse File delegieren. Diese wird vom HandlerManager erzeugt und zerstört. Ein FileHandle wird dann nicht als Pointer sondern per Value zurückgegeben (mit einem Zeiger auf File im Bauch und einem Zeiger auf den HandlerManager, falls nicht Singleton).
    Beim Erzeugen eines FileHandlers einen Referenzzähler in HandlerManager hochzählen und beim Zerstören wieder runterzählen. Wird 0 erreicht, zerstörst Du das assozierte File.
    Es erscheint mir nicht so ganz logisch, shared_ptr mit einem Manager zu verwalten (der meistens ja eine lange Lebensdauer hat), da damit die Logik des shared_ptr unterlaufen wird. Du müsstes beim Erreichen von einer Nutzung (wissend, dass Dein Manager damit derjenige ist, der den Zeiger noch weiss) das Objekt freigeben.



  • otze schau dir mal boost::intrusive_ptr an. Das ist eine Referenz zählender smart pointer du kannst aber add und release steuern. Ich bin mir aber jetzt nicht ganz sicher aber ich denke es sollte möglich sein das du das objekt beim HandleManager deregistrieren kannst wenn nur noch eine referenz da ist ( => Kann dann nur noch im Handle Manager sein )


  • Mod

    Es erscheint mir unlogisch zu versuchen, handles, die (möglicherweise) länger als der manager leben, mit diesem zu verwalten. Ein Logfile ist ja nun ein sehr spezielles file; möglicherweise macht es sinn, es völlig losgelöst vom filemanager zu behandeln (dieser muss lediglich dafür sorgen, dass er nicht selber handles auf dieses spezielle file herausgibt).



  • evilissimo schrieb:

    otze schau dir mal boost::intrusive_ptr an. Das ist eine Referenz zählender smart pointer du kannst aber add und release steuern. Ich bin mir aber jetzt nicht ganz sicher aber ich denke es sollte möglich sein das du das objekt beim HandleManager deregistrieren kannst wenn nur noch eine referenz da ist ( => Kann dann nur noch im Handle Manager sein )

    sicher, das geht. Das mach ich aber atm schon mit der angesprochenen kombination aus weak_ptr im container und für die rückgabe des handles shared_ptr(ich poste mal besser die Klasse, wie sie mit den veränderungen aussieht, da meine ausführungen der ausprobierten änderungen ja ignoriert wurde^^)

    //nochnet durch den compiler gejagt, geht nur ums prinzip
    class handleManager{
            private:
               typedef std::map<boost::filesystem::path,boost::weak_ptr<FileHandle> > Container;
               typedef Container::iterator Iterator; 
    
               Container handles;
    
               static void destructor(HandleManager* self,FileHandle* handle){
                   self->handles.erase(handle->getFilePath());
                   delete handle;
                }
            public:
                boost::shared_ptr<FileHandle> HandleManager::getHandle(const boost::filesystem::path& filePath){
                    Iterator pos=handles.find(path);
                    if(pos!=handles.end()){
                        return pos->second.Lock();
                    }
                    boost::shared_ptr<FileHandle> newHandle(new FileHandle(path),boost::bind(destructor,this,_1));
                    map.insert(std::make_pair(filePath,newHandle));
                    return newHandle;
                }
        };
    

    @CStoll bitte lies etwas weiter, zb bis zu dem punkt, an dem ich schreib, dass ich im container shared_ptr durch weak_ptr ersetze :p

    @Niemand

    Du hast nicht geschrieben, was die Klasse FileHandle macht.

    muss ich net, ich hätte in diesem beispiel auch Foo schreiben können 😉
    es geht nur darum, dass ich im programm selber nie genau weis, wieviele referenzen ich noch auf dieses Objekt habe. Aber um deinen wissensdurst zu befriedigen: wie du richtig vermutet hast, stellt handle nur einen dateizugriff dar. diesen benutzen dann einige objekte. Unter andrem ein File Objekt(welches nicht einmalig sein muss-erklärung spar ich mir, weil ich dann das ganze konzept der lib erklären müsste^^), und access objekte für die datei.

    @camper der manager verwaltet die handles nicht, sondern den zugriff auf diese. da die handles auf jede datei einmalig sein müssen, muss der manager wissen, auf welche dateien es handles gibt. wenn der manager kaputt geht, ist das net so schlimm. die verbliebenen handles funktionieren ganz normal, nur dass man halt keine neuen mehr erstellen sollte. Und nur mal so: wie soll ich bitteschön überprüfen, wofür handles verwendet werden ;).


  • Mod

    otze schrieb:

    @camper der manager verwaltet die handles nicht, sondern den zugriff auf diese. da die handles auf jede datei einmalig sein müssen, muss der manager wissen, auf welche dateien es handles gibt. wenn der manager kaputt geht, ist das net so schlimm. die verbliebenen handles funktionieren ganz normal, nur dass man halt keine neuen mehr erstellen sollte. Und nur mal so: wie soll ich bitteschön überprüfen, wofür handles verwendet werden ;).

    es kommt halt drauf an wofür der manager gut ist. in einer typischen situation hat ein system eine schnittstelle für logging zwecke, die generell vor irgendwelchen anderen dingen existiert und auch als letztes zerstört wird. dann kann der manager ohne weiteres mit dieser schnittstelle komunizieren und auf diese weise verhindern, jemals handles zu erzeugen, die mit dem logging kollidieren. zweitens kann der manager die einzigartigkeit von handles nur garantieren, wenn er erzeugt wird, bevor irgendwelche handles generiert werden (mit ausnahme solcher die zum logging benötigt werden s.o.) - dann sollte er auch erst zerstört werden, wenn keines dieser handles mehr existiert, andernfalls hast du vielleicht schon ein design problem. denn

    wenn der manager kaputt geht, ist das net so schlimm. die verbliebenen handles funktionieren ganz normal, nur dass man halt keine neuen mehr erstellen sollte.

    das impliziert ja, dass du auf irgeneine weise feststellen könntest, ob der manager noch existiert, oder nicht.

    soviel zur theorie :p - hier ein versuch:

    // dein FileHandle kennt ja den manager, von dem es erzeugt wurde, nutze boost::weak_ptr<HandleManager> dafür
    // der FileManager wird per boost::smart_ptr<HandleManager> gehalten
    static void destructor(boost::weak_ptr<HandleManager> self,FileHandle* handle){
        if ( boost::shared_ptr<HandleManager> selfself = self.lock() )
            selfself->handles.erase(handle->getFilePath());
        delete handle;
    }
    


  • camper schrieb:

    otze schrieb:

    @camper der manager verwaltet die handles nicht, sondern den zugriff auf diese. da die handles auf jede datei einmalig sein müssen, muss der manager wissen, auf welche dateien es handles gibt. wenn der manager kaputt geht, ist das net so schlimm. die verbliebenen handles funktionieren ganz normal, nur dass man halt keine neuen mehr erstellen sollte. Und nur mal so: wie soll ich bitteschön überprüfen, wofür handles verwendet werden ;).

    es kommt halt drauf an wofür der manager gut ist. in einer typischen situation hat ein system eine schnittstelle für logging zwecke, die generell vor irgendwelchen anderen dingen existiert und auch als letztes zerstört wird. dann kann der manager ohne weiteres mit dieser schnittstelle komunizieren und auf diese weise verhindern, jemals handles zu erzeugen, die mit dem logging kollidieren. zweitens kann der manager die einzigartigkeit von handles nur garantieren, wenn er erzeugt wird, bevor irgendwelche handles generiert werden (mit ausnahme solcher die zum logging benötigt werden s.o.) - dann sollte er auch erst zerstört werden, wenn keines dieser handles mehr existiert, andernfalls hast du vielleicht schon ein design problem. denn

    es gibt keine logging schnittstelle(zumindest noch nicht). aber da das ding dateien öffnet, kann ich ja bis sie wieder geschlossen werden reinschreiben.

    wenn der manager kaputt geht, ist das net so schlimm. die verbliebenen handles funktionieren ganz normal, nur dass man halt keine neuen mehr erstellen sollte.

    das impliziert ja, dass du auf irgeneine weise feststellen könntest, ob der manager noch existiert, oder nicht.

    nein, feststellen kann ichs net. es tritt einfach undefiniertes verhalten auf. wenn 2 handles auf ein und die selbe datei geöffnet werden, kann es laufen wie geplant oder die datei kann nicht ein weiteres mal geöffnet werden und ne exception fliegt, oder es wird ganz komisch. Deshalb sind die ctors von filehandle private und der manager ein friend 😉

    soviel zur theorie :p - hier ein versuch:

    // dein FileHandle kennt ja den manager, von dem es erzeugt wurde, nutze boost::weak_ptr<HandleManager> dafür
    // der FileManager wird per boost::smart_ptr<HandleManager> gehalten
    static void destructor(boost::weak_ptr<HandleManager> self,FileHandle* handle){
        if ( boost::shared_ptr<HandleManager> selfself = self.lock() )
            selfself->handles.erase(handle->getFilePath());
        delete handle;
    }
    

    das hat zuviele nachteile. allein der zwang, dass der Manager einen shared_ptr braucht ist böse. Aber der ansatz ist gut. Was hälste davon?

    static void destructor(HandleManager* self,boost::weak_ptr<bool> managerExists,FileHandle* handle){
        if(!managerExists.expired()){
            self->handles.erase(handle->getFilePath());
        }
        delete handle;
    }
    

  • Mod

    inwiefern macht dein ansatz einen unterschied? den overhead von smart_ptr/weak_ptr hast du ja trotzdem - nur dass der HandleManager jetzt selbst einen smart_ptr<bool> besitzt, anstatt selbst per smart_ptr zu existieren. wie setzt du den manager denn ein ? ich würde ja ein singleton benutzen und in diesem falle sehe ich keinen nachteil darin, den manager in einen smart_ptr zu packen.



  • camper schrieb:

    inwiefern macht dein ansatz einen unterschied? den overhead von smart_ptr/weak_ptr hast du ja trotzdem - nur dass der HandleManager jetzt selbst einen smart_ptr<bool> besitzt, anstatt selbst per smart_ptr zu existieren.

    Du hast es selbst gesagt 😉

    und zum thema singleton: ich bin da etwas von weg, singletons zu benutzen. vorallem in den grenzfällen programmbeginn/programmende find ich sie etwas zu schwierig einzusetzen. Aber was ich tun werde, ist sie schön wegzukapseln(ich bezweifle einfach mal, dass sich jemand mit den ungepufferten unheimlich komplexen filehandles ausseinandersetzen will, wenn er doch herrlich unkomplizierte File interfaces nutzen kann 😉


Anmelden zum Antworten