GNOME Bugzilla – Bug 645015
Crash in g_object_unref, image_download_cb
Last modified: 2011-08-15 16:22:29 UTC
Version: 0.13.3 What were you doing when the application crashed? Starting session, saved session contained rhythmbox Distribution: openSUSE 11.4 (x86_64) Gnome Release: 2.32.1 (null) (SUSE) BugBuddy Version: 2.32.0 System: Linux 2.6.37.1-1.2-desktop #1 SMP PREEMPT 2011-02-21 10:34:10 +0100 x86_64 X Vendor: The X.Org Foundation X Vendor Release: 10903000 Selinux: No Accessibility: Disabled GTK+ Theme: Gilouche Icon Theme: Gilouche GTK+ Modules: gnomesegvhandler, canberra-gtk-module Memory status: size: 909955072 vsize: 909955072 resident: 62144512 share: 28778496 rss: 62144512 rss_rlim: 18446744073709551615 CPU usage: start_time: 1300367964 rtime: 102 utime: 79 stime: 23 cutime:0 cstime: 0 timeout: 0 it_real_value: 0 frequency: 100 Backtrace was generated from '/usr/bin/rhythmbox' [Thread debugging using libthread_db enabled] [New Thread 0x7f0feba0d700 (LWP 4392)] [New Thread 0x7f0feb20c700 (LWP 4365)] [New Thread 0x7f0fdeeeb700 (LWP 4327)] [New Thread 0x7f0fdf6ec700 (LWP 4326)] [New Thread 0x7f0fdfeed700 (LWP 4325)] [New Thread 0x7f0fe06ee700 (LWP 4324)] [New Thread 0x7f0fe0eef700 (LWP 4323)] [New Thread 0x7f0fe16f0700 (LWP 4322)] [New Thread 0x7f0fe1ef1700 (LWP 4321)] [New Thread 0x7f0fe26f2700 (LWP 4320)] [New Thread 0x7f0fe2ef3700 (LWP 4319)] [New Thread 0x7f0fe36f4700 (LWP 4318)] [New Thread 0x7f0ff6823700 (LWP 4114)] 0x00007f1004820e6d in __libc_waitpid (pid=<value optimized out>, stat_loc=<value optimized out>, options=<value optimized out>) at ../sysdeps/unix/sysv/linux/waitpid.c:41 in ../sysdeps/unix/sysv/linux/waitpid.c
+ Trace 226353
Thread 1 (Thread 0x7f1006b95800 (LWP 4089))
Inferior 1 [process 4089] will be detached. Quit anyway? (y or n) [answered Y; input not from terminal] ---- Critical and fatal warnings logged during execution ---- ** GLib-GObject **: g_object_unref: assertion `G_IS_OBJECT (object)' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ** GdkPixbuf **: gdk_pixbuf_new_from_file_at_scale: assertion `filename != NULL' failed ----------- .xsession-errors --------------------- Missing separate debuginfo for /usr/lib64/python2.7/lib-dynload/zlib.so Try: zypper install -C "debuginfo(build-id)=42167cb089d4579637b654e4a658058706eef9ae" Missing separate debuginfo for /usr/lib64/python2.7/lib-dynload/_io.so Try: zypper install -C "debuginfo(build-id)=fd12fa5504b3694c3f0a6a1bfdafde5724c889a1" Missing separate debuginfo for /usr/lib64/python2.7/site-packages/gtk-2.0/gnomekeyring.so Try: zypper install -C "debuginfo(build-id)=cb01ab29619e6839202b7e4fbce49d531b9fe505" Missing separate debuginfo for /usr/lib64/python2.7/lib-dynload/grp.so Try: zypper install -C "debuginfo(build-id)=2baf5609989edfca3c449ae553a88c90c66910af" Missing separate debuginfo for /usr/lib64/libmtp.so.8 Try: zypper install -C "debuginfo(build-id)=f999ca3627b01f27e7c54d3f2143fecfb2d9beb3" Missing separate debuginfo for /usr/lib64/libusb-0.1.so.4 Try: zypper install -C "debuginfo(build-id)=780d855a97565edf4c744f7dd69595a5f4f7d776" Missing separate debuginfo for /usr/lib64/libusb-1.0.so.0 Try: zypper install -C "debuginfo(build-id)=7a7220cc354fd81578f4de64ecd6281f176809e6" 41 ../sysdeps/unix/sysv/linux/waitpid.c: Adresář nebo soubor neexistuje. --------------------------------------------------
Jamie, any ideas about this? It's not immediately obvious why it'd be crashing, and I can't see why we'd be calling gdk_pixbuf_new_from_file_at_scale with a bad filename either.
Not got a clue. I can't pretend that that's a particularly well written function (or entire file), but I don't understand why this crash has happened. I'm going to be crazy busy this next week--my uni project is due on friday--but after that I can try to give it a more in depth look. On the off-chance that the reporter hasn't used last.fm in rhythmbox since the crash happened, perhaps the contents of ~/.cache/rhythmbox could be useful.
I used rhythmbox after that crash. I just disabled booklet plugin and not seen crash any more. Now I re-enabled it and it does not crash as well, however some images are not found and waiting animation is displayed. So the crash is not reproducible. Note that I don't have any last.fm profile photo uploaded. Log shows lots of these errors: (rhythmbox:31971): Rhythmbox-WARNING **: eel_strdup_strftime does not support non-standard escape code %e
*** Bug 650954 has been marked as a duplicate of this bug. ***
*** Bug 652815 has been marked as a duplicate of this bug. ***
Spent some time with my brother thinking this through and I think we've found the problem: We initially make an array of RBAudioscrobblerUserData, eg my top tracks, from either the cache or a request to last.fm. If a track is missing an image a download is started. Suppose whilst the image is downloading the profile gets refreshed. A new top tracks request is sent. In top_tracks_response_cb (or equivalent): the old array of data is freed so the new array can be stored. The image being downloaded is for an RBAudioscrobblerUserData which no longer exists. image_download_cb: Image download finishes, looks up data in file_to_data_map, but this is now garbage. Meaning data->image could be non-null but also not a valid gobject => g_object_unref segfaults. calculate_cached_image_path: data is garbage meaning that data->type could be anything. The lack of a default block means image path remains NULL. This explains the warnings from GdkPixbuf. Fixes I can think of are: a) Cancel the download before the RBAudioscrobblerUserData is freed. b) Again, before the RBAudioscrobblerUserData is freed, remove the download file from the file_to_data_map. Then in the callback handle such a case properly (by not crashing). The second has the advantage that the download is allowed to finish. If this sounds sensible then patch forthcoming.
Yes, that sounds sensible. I think allowing the download to finish would be better. Looking at this code again, I suspect we're leaking a reference to the source file for the downloads too.
In download_image (), where I've written src_file = g_file_new_for_uri (image_url); /* only start a download if the file is not already being downloaded */ if (g_hash_table_lookup (user->priv->file_to_data_map, src_file) == NULL) { <snip> } does that actually make any sense? Even if that image url is being downloaded already, it'll be a different GFile object surely? But suppose we were to actually do what this line is intended to, by having a url to gfile map and checking if a url lookup succeeds. Then if we were to do something along the lines of: /* only start a download if the file is not already being downloaded */ src_file = g_hash_table_lookup (user->priv->url_to_file_map, image_url) if (src_file == NULL) { /* download file */ } else { /* file's old data points to garbage. replace with new one */ g_hash_table_insert (user->priv->file_to_data_map, src_file, data); } I think this would also fix the bug, but I'm not 100% confident, and it's making my head hurt. The callback wouldn't need to be edited (except to unref src_file, as you mentioned), nor would the array deletion code. What do you think?
(In reply to comment #8) > In download_image (), where I've written > > src_file = g_file_new_for_uri (image_url); > > /* only start a download if the file is not already being downloaded */ > if (g_hash_table_lookup (user->priv->file_to_data_map, src_file) == NULL) { > <snip> > } > > does that actually make any sense? Even if that image url is being downloaded > already, it'll be a different GFile object surely? Yes.. we could use g_file_hash and g_file_equal as the hash and equal functions for this hash table. Probably use g_object_unref as the key destroy function too. > But suppose we were to actually do what this line is intended to, by having a > url to gfile map and checking if a url lookup succeeds. Then if we were to do > something along the lines of: > > /* only start a download if the file is not already being downloaded */ > src_file = g_hash_table_lookup (user->priv->url_to_file_map, image_url) > if (src_file == NULL) { > /* download file */ > } else { > /* file's old data points to garbage. replace with new one */ > g_hash_table_insert (user->priv->file_to_data_map, src_file, data); > } > > I think this would also fix the bug, but I'm not 100% confident, and it's > making my head hurt. > The callback wouldn't need to be edited (except to unref src_file, as you > mentioned), nor would the array deletion code. > > What do you think? I think we aren't guaranteed that an image that was needed before the top tracks response (or whatever) would be needed afterwards, so this wouldn't be enough by itself. It seems like making RBAudioscrobblerUserData reference counted would fix this - the pointer arrays would have one reference, the download map would have another. The download callback could check if the other reference was still alive before emitting the updated signals etc.
*** Bug 652989 has been marked as a duplicate of this bug. ***
Created attachment 190294 [details] [review] audioscrobbler: ensure RBAudioscrobblerUserData stays alive for image download Make RBAudioscrobblerUserData refcounted and give the download map a reference. Also make download maps responsible for unreffing their keys and values, and fix a couple leaks.
Oops. Just to explain the patch a bit more in depth. I've basically done what you suggested in comment 9, and a couple other memory management fixes. I have not done what I suggested in comment 8 as there can be more than one valid Data for each src_file. To fix this properly I think file_to_data_map would need its values to be lists of RBAudioscrobblerUserDatas rather than single ones. When the hash table lookup is succesful in download_image, append the data to the list. And in image_download_cb update the pixbuf and emit signals for each element (with refcount > 1). For the time being though this should stop the segfault, but images will be missing for items if they were not the first item to require a download of the same image.
Created attachment 190321 [details] [review] audioscrobbler: keep RBAudioscrobblerUserData alive for image download Alternatively this patch keeps the hash tables using g_direct_hash instead of g_file_hash. The hash table lookup in download image is removed as it doesn't work with direct hash. Refcounting should fix the bug, and every item with an available image will download and display it. There will be multiple downloads of the same image, however.
I'm OK with either of these, but I think I prefer the first one.
Review of attachment 190321 [details] [review]: ok
Review of attachment 190294 [details] [review]: also ok
I'm backpacking at the moment and my only internet access is the occasional internet cafe. So I wont be able to commit anything for the next 3 or 4 weeks.
I've just pushed the first patch to master. I don't have permissions to mark the patches appropriately or close the bug, however.
Jamie, you should now be able to mark the patches as committed and to close the bug
Thanks! 1st patch commited, 2nd rejected. Bug fixed.