Bug 117541 - JuK crashes on shutdown with glibc memory error.
Summary: JuK crashes on shutdown with glibc memory error.
Status: RESOLVED FIXED
Alias: None
Product: juk
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: Compiled Sources Linux
: NOR crash
Target Milestone: ---
Assignee: Michael Pyne
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-02 22:33 UTC by Michael Pyne
Modified: 2005-12-02 22:45 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Pyne 2005-12-02 22:33:06 UTC
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.
Comment 1 Michael Pyne 2005-12-02 22:42:49 UTC
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.
Comment 2 Michael Pyne 2005-12-02 22:45:37 UTC
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.