Hi, 2013/11/30 Kevin Funk: > Hey guys, > > My recent attempts to make KDevelop shine on Windows revealed some odd > behaviour inside kdelibs. > > I need some help in order to find the right approach to fix an at least 3 year > old bug [1] ultimately caused by the use of a static member object in the > KCompTreeNode class (kcompletion.h) in kdelibs. Please see the attached bug > report [2]. > > Note that the error *only shows up on Windows*, on Linux we don't have this > problem. I'll try to explain what's wrong in this mail. > > The actual issue: > - KDevelop running: > - We have an instance of KateGlobal owned by KateFactory, > a plugin loaded by KDevelop > - Some descendant of KateGlobal (KateCmd) owns a KCompTreeNode instance > - KCompTreeNode uses a custom allocator for creating/deleting instances > of itself, holds a static member of type KZoneAllocactor for that > - KZoneAllocator holds the memory for all the instantiated KCompTreeNodes > > So we have the following object ownership (some levels omitted): > - KateFactory > KateGlobal > KCompTreeNode > KZoneAllocator (static) > > When closing KDevelop *under Linux*: > - ~KateFactory, via QObjectCleanupHandler during __run_exit_handlers (glibc) > ~KateGlobal, via call to KateGlobal::decRef() > ~KCompTreeNode > - ~KZoneAllocator, during __cxa__finalize (glibc) > > The order is fine in this case. No segfaults. > > However, on Windows the destruction order is different (and I cannot really > explain why). When closing KDevelop *under Windows*: > - ~KZoneAllocator is called first, via > "kdeui.dll!`dynamic atexit destructor for 'KCompTreeNode::alloc''()" > - ~KateFactory, via QObjectCleanupHandler > ~KateGlobal > Call to KCompTreeNode::removeItem() > > => Crash because memory for the KCompTreeNode instances was already freed > by ~KZoneAllocator > > > I wonder how to fix that: > The easiest solution obviously: Get rid off the custom allocator in > KCompTreeNode. Having a class owning its custom allocator is just plain wrong, > right? But removing that probably has performance impacts. (Does anyone have > benchmarks here?)
well, having a custom allocator may have some benefits. I guess that KCompletion uses one to ensure that all KCompTreeNodes are close to each other in memory and thus to reduce cache misses? I don't know how much dropping the custom allocator would affect the performance, one would have to do some benchmarking with typical KCompletion use cases. > Just using K_GLOBAL_STATIC for the KZoneAllocator instance doesn't help > either. I get the same destruction order + crash with that on Windows. > > Dynamic allocation inside KCompTreeNode doesn't work either, because > KZoneAllocator needs to be there *before* the first KCompTreeNode is created > and needs to there *until* the last one has been destroyed. > > Any ideas? Well, here is one idea (untested): use a QSharedPointer<KZoneAllocator> to ensure that the KZoneAllocator is not destroyed before the last KCompletion instance using it. Replace static KZoneAllocator alloc; in KCompTreeNode by static QSharedPointer<KZoneAllocator> alloc; and, in kcompletion.cpp, KZoneAllocator KCompTreeNode::alloc(8192); by QSharedPointer<KZoneAllocator> KCompTreeNode::alloc; In KCompTreeNode's operator new/delete, replace "alloc." by "alloc->". Furthermore, add a QSharedPointer<KZoneAllocator> member to KCompletionPrivate. In KCompletionPrivate's constructor, first check if KCompTreeNode::alloc isNull(), initialize it if that is not the case, and then copy "alloc" to the new member. I think that this might work, and it might also improve application start-up time because the KZoneAllocator would only be initialized on first use of a KCompletion object. Cheers, Frank >> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<