JuK can crash on shutdown when using a glibc with memory corruption detection. I've been fighting this bug for months and finally found it's cause. :) Basically, when you quit JuK, glibc will crash the program just before shutdown (sometimes before KConfig saves in my experience). console output: juk: [void JuK::slotQuit()] juk: [virtual bool JuK::queryExit()] juk: [void CoverManagerPrivate::saveCovers() const] juk: Opening covers db: /home/kde-cvs/.kde3.3/share/apps/juk/coverdb/covers kio (KDirLister): -KDirLister kio (KDirLister): [virtual void KDirLister::stop()] kio (KDirListerCache): [void KDirListerCache::stop(KDirLister*)] lister: 0x82843c8 kio (KDirListerCache): [void KDirListerCache::forgetDirs(KDirLister*)] 0x82843c8 kio (KDirListerCache): [void KDirListerCache::forgetDirs(KDirLister*, const KURL&, bool)] 0x82843c8 _url: file:///home/kde-cvs/Music kio (KDirListerCache): [void KDirListerCache::forgetDirs(KDirLister*, const KURL&, bool)] 0x82843c8 item moved into cache: file:///home/kde-cvs/Music *** glibc detected *** corrupted double-linked list: 0x08632388 *** Expected behavior is a clean shutdown. Workaround: Due to the cause (I will describe the cause in an additional comment), you can avoid the bug by stopping playback and clicking on every playlist in the list of playlists before closing JuK.
The cause of this bug is one of those very subtle C++ things. The simple explanation is that the Playlist::insertItem(QListViewItem *item) function is called during the creation of a PlaylistItem. insertItem() adds item to a list of PlaylistItems to process on-demand by using a guarded pointer. PlaylistItem includes logic to determine when it is being watched by a guarded pointer, and will update the pointer automatically in its destructor. The problem is the order of execution, and the fact that item *isn't* a PlaylistItem yet. order: the "new PlaylistItem" command is called QListViewItem::QListViewItem(QListView *lv) is called (the new item is a QListViewItem now, not a PlaylistItem) lv->insertItem(QListViewItem *item) is called to insert the new item into the parent list view. Note that even though we called "new PlaylistItem", the pointer doesn't point to a PlaylistItem yet, just a QListViewItem. insertItem is resolved to Playlist::insertItem, which casts the pointer to PlaylistItem * using static_cast<>. If this cast had been performed using dynamic_cast<>, the bug would have been caught a long time ago, as dynamic_cast<> returns 0 since item isn't (yet) a PlaylistItem. The PlaylistItem* is used to create the guarded pointer, which updates the item and set the "watched" flag. KListViewItem::KListViewItem() is run. The new item is now a KListViewItem. PlaylistItem::PlaylistItem() is finally run, and *resets* the watched flag as part of initialization. We now have a dangling guarded pointer, and the new item is *finally* a PlaylistItem. When the newly created PlaylistItem is finally deleted, it checks for the watched flag before removing guarded pointers. Since the item reset the flag, it doesn't think it is watched, and doesn't remove the guard. So another way to detect the bug would have been to clear any guarded pointers unconditionally. Then, the guarded pointer is destructed, and tries to delete the old item (which it is technically still watching). This causes a double-free error, and glibc detects this and causes an abort. Whew! I will commit a fix shortly.
SVN commit 485102 by mpyne: Fix bug 117541 (JuK crashes on shutdown due to memory corruption). The fix is simple, the cause is complex. The bug report has the details if you want to read it. BUG:117541 M +4 -2 playlist.cpp M +2 -2 playlist.h --- branches/KDE/3.5/kdemultimedia/juk/playlist.cpp #485101:485102 @@ -438,7 +438,7 @@ // Since this method gets a lot of traffic, let's optimize for such. if(!m_addTime.isEmpty()) { - for(QValueList<PlaylistItem::Pointer>::ConstIterator it = m_addTime.begin(); + for(PlaylistItemList::ConstIterator it = m_addTime.begin(); it != m_addTime.end(); ++it) { if(*it) @@ -449,7 +449,7 @@ } if(!m_subtractTime.isEmpty()) { - for(QValueList<PlaylistItem::Pointer>::ConstIterator it = m_subtractTime.begin(); + for(PlaylistItemList::ConstIterator it = m_subtractTime.begin(); it != m_subtractTime.end(); ++it) { if(*it) @@ -574,6 +574,8 @@ m_search.clearItem(item); m_history.remove(item); + m_addTime.remove(item); + m_subtractTime.remove(item); delete item; if(emitChanged) --- branches/KDE/3.5/kdemultimedia/juk/playlist.h #485101:485102 @@ -647,8 +647,8 @@ bool m_disableColumnWidthUpdates; mutable int m_time; - mutable QValueList<PlaylistItem::Pointer> m_addTime; - mutable QValueList<PlaylistItem::Pointer> m_subtractTime; + mutable PlaylistItemList m_addTime; + mutable PlaylistItemList m_subtractTime; /** * The average minimum widths of columns to be used in balancing calculations.