From daa8f15ce956ada22326cb23a3c922c29278a0d2 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 28 Mar 2018 21:47:10 +0300 Subject: [PATCH 1/9] hammond-gtk::utils: change the signature of get_pixbug_from_path and rename it Requires a gtk::Image as argument now, it sets the pixbuf to the img directly instead of returning it. New name is set_image_from_path. This is ground work so we can later keep the image reference, and use it to set the image with a callback. --- hammond-gtk/src/utils.rs | 18 ++++++++++++++---- hammond-gtk/src/views/episodes.rs | 6 ++---- hammond-gtk/src/views/shows.rs | 6 ++---- hammond-gtk/src/widgets/show.rs | 6 ++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index f8a6bcb..65a24d3 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -1,8 +1,11 @@ #![cfg_attr(feature = "cargo-clippy", allow(type_complexity))] -use failure::Error; use gdk_pixbuf::Pixbuf; use gio::{Settings, SettingsExt}; +use gtk; +use gtk::prelude::*; + +use failure::Error; use regex::Regex; use reqwest; use send_cell::SendCell; @@ -137,7 +140,11 @@ lazy_static! { // GObjects do not implement Send trait, so SendCell is a way around that. // Also lazy_static requires Sync trait, so that's what the mutexes are. // TODO: maybe use something that would just scale to requested size? -pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Result { +pub fn set_image_from_path( + image: >k::Image, + pd: &PodcastCoverQuery, + size: u32, +) -> Result<(), Error> { { let hashmap = CACHED_PIXBUFS .read() @@ -145,7 +152,9 @@ pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Result Result Result<(), Error> { let pd = dbqueries::get_podcast_cover_from_id(podcast_id)?; - let img = get_pixbuf_from_path(&pd, 64)?; - self.image.set_from_pixbuf(&img); - Ok(()) + set_image_from_path(&self.image, &pd, 64) } } diff --git a/hammond-gtk/src/views/shows.rs b/hammond-gtk/src/views/shows.rs index 2a59984..1abf4c8 100644 --- a/hammond-gtk/src/views/shows.rs +++ b/hammond-gtk/src/views/shows.rs @@ -6,7 +6,7 @@ use hammond_data::Podcast; use hammond_data::dbqueries; use app::Action; -use utils::{get_ignored_shows, get_pixbuf_from_path}; +use utils::{get_ignored_shows, set_image_from_path}; use std::sync::Arc; use std::sync::mpsc::Sender; @@ -133,8 +133,6 @@ impl ShowsChild { } fn set_cover(&self, pd: Arc) -> Result<(), Error> { - let image = get_pixbuf_from_path(&pd.clone().into(), 256)?; - self.cover.set_from_pixbuf(&image); - Ok(()) + set_image_from_path(&self.cover, &pd.clone().into(), 256) } } diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 5a304fe..c6b458f 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -10,7 +10,7 @@ use hammond_data::dbqueries; use hammond_data::utils::replace_extra_spaces; use app::Action; -use utils::get_pixbuf_from_path; +use utils::set_image_from_path; use widgets::episode::episodes_listbox; use std::sync::Arc; @@ -113,9 +113,7 @@ impl ShowWidget { /// Set the show cover. fn set_cover(&self, pd: Arc) -> Result<(), Error> { - let image = get_pixbuf_from_path(&pd.into(), 128)?; - self.cover.set_from_pixbuf(&image); - Ok(()) + set_image_from_path(&self.cover, &pd.into(), 128) } /// Set the descripton text. From badcbc32c6ac9095a916ef3207d67904ac83a45f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 28 Mar 2018 22:41:45 +0300 Subject: [PATCH 2/9] Implement async loading of the Show covers. --- hammond-gtk/src/utils.rs | 36 +++++++++++++++++++++++-------- hammond-gtk/src/views/episodes.rs | 5 +++-- hammond-gtk/src/views/shows.rs | 18 ++++++++-------- hammond-gtk/src/widgets/show.rs | 2 +- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 65a24d3..5c332d9 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -2,6 +2,7 @@ use gdk_pixbuf::Pixbuf; use gio::{Settings, SettingsExt}; +use glib; use gtk; use gtk::prelude::*; @@ -21,7 +22,7 @@ use hammond_downloader::downloader; use std::collections::{HashMap, HashSet}; use std::sync::{Mutex, RwLock}; use std::sync::Arc; -use std::sync::mpsc::Sender; +use std::sync::mpsc::*; use std::thread; use app::Action; @@ -142,7 +143,7 @@ lazy_static! { // TODO: maybe use something that would just scale to requested size? pub fn set_image_from_path( image: >k::Image, - pd: &PodcastCoverQuery, + pd: Arc, size: u32, ) -> Result<(), Error> { { @@ -158,13 +159,30 @@ pub fn set_image_from_path( } } - let img_path = downloader::cache_image(pd)?; - let px = Pixbuf::new_from_file_at_scale(&img_path, size as i32, size as i32, true)?; - let mut hashmap = CACHED_PIXBUFS - .write() - .map_err(|_| format_err!("Failed to lock pixbuf mutex."))?; - hashmap.insert((pd.id(), size), Mutex::new(SendCell::new(px.clone()))); - image.set_from_pixbuf(&px); + let (sender, receiver) = channel(); + let pd_ = pd.clone(); + thread::spawn(move || { + sender.send(downloader::cache_image(&pd_)).unwrap(); + }); + + let image = image.clone(); + gtk::timeout_add(200, move || { + if let Ok(path) = receiver.try_recv() { + if let Ok(path) = path { + if let Ok(px) = + Pixbuf::new_from_file_at_scale(&path, size as i32, size as i32, true) + { + if let Ok(mut hashmap) = CACHED_PIXBUFS.write() { + hashmap.insert((pd.id(), size), Mutex::new(SendCell::new(px.clone()))); + image.set_from_pixbuf(&px); + } + } + } + glib::Continue(false) + } else { + glib::Continue(true) + } + }); Ok(()) } diff --git a/hammond-gtk/src/views/episodes.rs b/hammond-gtk/src/views/episodes.rs index 5e3c7a0..e8f51c5 100644 --- a/hammond-gtk/src/views/episodes.rs +++ b/hammond-gtk/src/views/episodes.rs @@ -10,6 +10,7 @@ use app::Action; use utils::{get_ignored_shows, set_image_from_path}; use widgets::EpisodeWidget; +use std::sync::Arc; use std::sync::mpsc::Sender; #[derive(Debug, Clone)] @@ -227,7 +228,7 @@ impl EpisodesViewWidget { } fn set_cover(&self, podcast_id: i32) -> Result<(), Error> { - let pd = dbqueries::get_podcast_cover_from_id(podcast_id)?; - set_image_from_path(&self.image, &pd, 64) + let pd = Arc::new(dbqueries::get_podcast_cover_from_id(podcast_id)?); + set_image_from_path(&self.image, pd, 64) } } diff --git a/hammond-gtk/src/views/shows.rs b/hammond-gtk/src/views/shows.rs index 1abf4c8..67ba529 100644 --- a/hammond-gtk/src/views/shows.rs +++ b/hammond-gtk/src/views/shows.rs @@ -2,7 +2,7 @@ use failure::Error; use gtk; use gtk::prelude::*; -use hammond_data::Podcast; +use hammond_data::{Podcast, PodcastCoverQuery}; use hammond_data::dbqueries; use app::Action; @@ -57,7 +57,7 @@ impl ShowsPopulated { let ignore = get_ignored_shows()?; let podcasts = dbqueries::get_podcasts_filter(&ignore)?; - podcasts.into_iter().map(Arc::new).for_each(|parent| { + podcasts.into_iter().for_each(|parent| { let flowbox_child = ShowsChild::new(parent); self.flowbox.add(&flowbox_child.child); }); @@ -116,23 +116,23 @@ impl Default for ShowsChild { } impl ShowsChild { - pub fn new(pd: Arc) -> ShowsChild { + pub fn new(pd: Podcast) -> ShowsChild { let child = ShowsChild::default(); child.init(pd); child } - fn init(&self, pd: Arc) { + fn init(&self, pd: Podcast) { self.container.set_tooltip_text(pd.title()); + WidgetExt::set_name(&self.child, &pd.id().to_string()); - if let Err(err) = self.set_cover(pd.clone()) { + let pd = Arc::new(pd.into()); + if let Err(err) = self.set_cover(pd) { error!("Failed to set a cover: {}", err) } - - WidgetExt::set_name(&self.child, &pd.id().to_string()); } - fn set_cover(&self, pd: Arc) -> Result<(), Error> { - set_image_from_path(&self.cover, &pd.clone().into(), 256) + fn set_cover(&self, pd: Arc) -> Result<(), Error> { + set_image_from_path(&self.cover, pd, 256) } } diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index c6b458f..ca5ca04 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -113,7 +113,7 @@ impl ShowWidget { /// Set the show cover. fn set_cover(&self, pd: Arc) -> Result<(), Error> { - set_image_from_path(&self.cover, &pd.into(), 128) + set_image_from_path(&self.cover, Arc::new(pd.into()), 128) } /// Set the descripton text. From 88cc7e6fecdaab56e25242e65e39f96d2f33ed8a Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 09:21:49 +0300 Subject: [PATCH 3/9] Fix set_image_from_path test --- hammond-gtk/src/utils.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 5c332d9..5c6c70f 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -253,7 +253,7 @@ mod tests { // This test inserts an rss feed to your `XDG_DATA/hammond/hammond.db` so we make it explicit // to run it. #[ignore] - fn test_get_pixbuf_from_path() { + fn test_set_image_from_path() { let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff"; // Create and index a source let source = Source::from_url(url).unwrap(); @@ -262,8 +262,9 @@ mod tests { pipeline::run(vec![source], true).unwrap(); // Get the Podcast - let pd = dbqueries::get_podcast_from_source_id(sid).unwrap(); - let pxbuf = get_pixbuf_from_path(&pd.into(), 256); + let img = gtk::Image::new(); + let pd = dbqueries::get_podcast_from_source_id(sid).unwrap().into(); + let pxbuf = set_image_from_path(&img, Arc::new(pd), 256); assert!(pxbuf.is_ok()); } From 87034700108594f746f38740e0524ed8b586cae4 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 10:24:02 +0300 Subject: [PATCH 4/9] h-gtk/utils: Use a threadpool to avoid spawning a million threads --- hammond-gtk/src/main.rs | 3 +-- hammond-gtk/src/utils.rs | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index d865cfb..a648f9b 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -26,15 +26,14 @@ extern crate hammond_downloader; extern crate humansize; extern crate loggerv; extern crate open; +extern crate rayon; extern crate regex; extern crate reqwest; extern crate send_cell; extern crate serde_json; extern crate take_mut; extern crate url; -// extern crate rayon; -// use rayon::prelude::*; use log::Level; use gtk::prelude::*; diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 5c6c70f..92bae9f 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -7,6 +7,7 @@ use gtk; use gtk::prelude::*; use failure::Error; +use rayon; use regex::Regex; use reqwest; use send_cell::SendCell; @@ -132,6 +133,7 @@ fn refresh_feed(source: Option>, sender: Sender) -> Result<( lazy_static! { static ref CACHED_PIXBUFS: RwLock>>> = { RwLock::new(HashMap::new()) }; + static ref THREADPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new().build().unwrap(); } // Since gdk_pixbuf::Pixbuf is refference counted and every episode, @@ -161,12 +163,12 @@ pub fn set_image_from_path( let (sender, receiver) = channel(); let pd_ = pd.clone(); - thread::spawn(move || { + THREADPOOL.spawn(move || { sender.send(downloader::cache_image(&pd_)).unwrap(); }); let image = image.clone(); - gtk::timeout_add(200, move || { + gtk::timeout_add(50, move || { if let Ok(path) = receiver.try_recv() { if let Ok(path) = path { if let Ok(px) = From c3658080d305c26b9fb48c39b8c1a5a594fa6304 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 11:37:31 +0300 Subject: [PATCH 5/9] Comment out a test. --- hammond-gtk/src/utils.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 92bae9f..fb471bf 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -168,12 +168,11 @@ pub fn set_image_from_path( }); let image = image.clone(); + let s = size as i32; gtk::timeout_add(50, move || { if let Ok(path) = receiver.try_recv() { if let Ok(path) = path { - if let Ok(px) = - Pixbuf::new_from_file_at_scale(&path, size as i32, size as i32, true) - { + if let Ok(px) = Pixbuf::new_from_file_at_scale(&path, s, s, true) { if let Ok(mut hashmap) = CACHED_PIXBUFS.write() { hashmap.insert((pd.id(), size), Mutex::new(SendCell::new(px.clone()))); image.set_from_pixbuf(&px); @@ -251,24 +250,25 @@ mod tests { assert_eq!(time, time_period_to_duration(time, "seconds").num_seconds()); } - #[test] + // #[test] // This test inserts an rss feed to your `XDG_DATA/hammond/hammond.db` so we make it explicit // to run it. - #[ignore] - fn test_set_image_from_path() { - let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff"; - // Create and index a source - let source = Source::from_url(url).unwrap(); - // Copy it's id - let sid = source.id(); - pipeline::run(vec![source], true).unwrap(); + // #[ignore] + // Disabled till https://gitlab.gnome.org/alatiera/Hammond/issues/56 + // fn test_set_image_from_path() { + // let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff"; + // Create and index a source + // let source = Source::from_url(url).unwrap(); + // Copy it's id + // let sid = source.id(); + // pipeline::run(vec![source], true).unwrap(); - // Get the Podcast - let img = gtk::Image::new(); - let pd = dbqueries::get_podcast_from_source_id(sid).unwrap().into(); - let pxbuf = set_image_from_path(&img, Arc::new(pd), 256); - assert!(pxbuf.is_ok()); - } + // Get the Podcast + // let img = gtk::Image::new(); + // let pd = dbqueries::get_podcast_from_source_id(sid).unwrap().into(); + // let pxbuf = set_image_from_path(&img, Arc::new(pd), 256); + // assert!(pxbuf.is_ok()); + // } #[test] fn test_itunes_to_rss() { From e203815f4f875b02e192277948c2bd9726602e01 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 13:07:46 +0300 Subject: [PATCH 6/9] hammond-gtk/utils.rs: Use a hashset to keep track of cover downloads. Use a HashSet to check if a download of a cover is already active. If it is, schedule a callback that will try to set the image from the cached pixbuf later. --- hammond-gtk/src/utils.rs | 44 +++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index fb471bf..76efbcd 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -133,7 +133,11 @@ fn refresh_feed(source: Option>, sender: Sender) -> Result<( lazy_static! { static ref CACHED_PIXBUFS: RwLock>>> = { RwLock::new(HashMap::new()) }; - static ref THREADPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new().build().unwrap(); + static ref COVER_DL_REGISTRY: RwLock> = RwLock::new(HashSet::new()); + static ref THREADPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new() + .breadth_first() + .build() + .unwrap(); } // Since gdk_pixbuf::Pixbuf is refference counted and every episode, @@ -148,6 +152,26 @@ pub fn set_image_from_path( pd: Arc, size: u32, ) -> Result<(), Error> { + { + // Check if there's an active download about this show cover. + // If there is, a callback will be set so this function will be called again. + // If the download succedes, there should be a quick return from the pixbuf cache_image + // If it fails another download will be scheduled. + let reg_guard = COVER_DL_REGISTRY + .read() + .map_err(|err| format_err!("Cover Registry: {}.", err))?; + if reg_guard.contains(&pd.id()) { + gtk::timeout_add( + 250, + clone!(image, pd => move || { + let _ = set_image_from_path(&image, pd.clone(), size); + glib::Continue(false) + }), + ); + return Ok(()); + } + } + { let hashmap = CACHED_PIXBUFS .read() @@ -164,12 +188,22 @@ pub fn set_image_from_path( let (sender, receiver) = channel(); let pd_ = pd.clone(); THREADPOOL.spawn(move || { - sender.send(downloader::cache_image(&pd_)).unwrap(); + { + if let Ok(mut guard) = COVER_DL_REGISTRY.write() { + guard.insert(pd_.id()); + } + } + let _ = sender.send(downloader::cache_image(&pd_)); + { + if let Ok(mut guard) = COVER_DL_REGISTRY.write() { + guard.remove(&pd_.id()); + } + } }); let image = image.clone(); let s = size as i32; - gtk::timeout_add(50, move || { + gtk::timeout_add(25, move || { if let Ok(path) = receiver.try_recv() { if let Ok(path) = path { if let Ok(px) = Pixbuf::new_from_file_at_scale(&path, s, s, true) { @@ -229,8 +263,8 @@ pub fn time_period_to_duration(time: i64, period: &str) -> Duration { #[cfg(test)] mod tests { use super::*; - use hammond_data::Source; - use hammond_data::dbqueries; + // use hammond_data::Source; + // use hammond_data::dbqueries; #[test] fn test_time_period_to_duration() { From 6071c664e70dcabd5ba0eb70c1851421604c606f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 13:32:57 +0300 Subject: [PATCH 7/9] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 882e8be..4b2a0d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +* Downlaoding and loading images now is done asynchronously and is not blocking programs execution. +[#7](https://gitlab.gnome.org/alatiera/Hammond/issues/7) ## [0.3.1] - 2018-03-28 From 710a3f25528675414a7dcf717372d449301278af Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 15:19:13 +0300 Subject: [PATCH 8/9] Use SendCell::try_get instead of SendCell::into_inner --- hammond-gtk/src/utils.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 76efbcd..391683d 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -179,8 +179,10 @@ pub fn set_image_from_path( if let Some(px) = hashmap.get(&(pd.id(), size)) { let m = px.lock() .map_err(|_| format_err!("Failed to lock pixbuf mutex."))?; - let px = m.clone().into_inner(); - image.set_from_pixbuf(&px); + let px = m.try_get().ok_or_else(|| { + format_err!("Pixbuf was accessed from a different thread than created") + })?; + image.set_from_pixbuf(px); return Ok(()); } } From f2444f151cd04dee9ec8f2b169bac4fa62c36d6f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 29 Mar 2018 15:26:44 +0300 Subject: [PATCH 9/9] h-gtk/utils: Re-work format_err! calls and improve formatting --- hammond-data/src/models/new_podcast.rs | 6 +++--- hammond-gtk/src/utils.rs | 16 +++++++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/hammond-data/src/models/new_podcast.rs b/hammond-data/src/models/new_podcast.rs index 4eb3ac6..97aaaa0 100644 --- a/hammond-data/src/models/new_podcast.rs +++ b/hammond-data/src/models/new_podcast.rs @@ -231,9 +231,9 @@ mod tests { love, a crashed UFO, an alien body, and an impossible heist unlike any \ ever attempted - scripted by Mac Rogers, the award-winning playwright \ and writer of the multi-million download The Message and LifeAfter.

"; - let img = "https://dfkfj8j276wwv.cloudfront.net/images/2c/5f/a0/1a/2c5fa01a-ae78-4a8c-\ - b183-7311d2e436c3/b3a4aa57a576bb662191f2a6bc2a436c8c4ae256ecffaff5c4c54fd42e\ - 923914941c264d01efb1833234b52c9530e67d28a8cebbe3d11a4bc0fbbdf13ecdf1c3.jpeg"; + let img = "https://dfkfj8j276wwv.cloudfront.net/images/2c/5f/a0/1a/2c5fa01a-ae78-4a8c-\ + b183-7311d2e436c3/b3a4aa57a576bb662191f2a6bc2a436c8c4ae256ecffaff5c4c54fd42e\ + 923914941c264d01efb1833234b52c9530e67d28a8cebbe3d11a4bc0fbbdf13ecdf1c3.jpeg"; NewPodcastBuilder::default() .title("Steal the Stars") diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 391683d..247dd26 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -161,13 +161,11 @@ pub fn set_image_from_path( .read() .map_err(|err| format_err!("Cover Registry: {}.", err))?; if reg_guard.contains(&pd.id()) { - gtk::timeout_add( - 250, - clone!(image, pd => move || { - let _ = set_image_from_path(&image, pd.clone(), size); - glib::Continue(false) - }), - ); + let callback = clone!(image, pd => move || { + let _ = set_image_from_path(&image, pd.clone(), size); + glib::Continue(false) + }); + gtk::timeout_add(250, callback); return Ok(()); } } @@ -175,10 +173,10 @@ pub fn set_image_from_path( { let hashmap = CACHED_PIXBUFS .read() - .map_err(|_| format_err!("Failed to get a lock on the pixbuf cache mutex."))?; + .map_err(|err| format_err!("Pixbuf HashMap: {}", err))?; if let Some(px) = hashmap.get(&(pd.id(), size)) { let m = px.lock() - .map_err(|_| format_err!("Failed to lock pixbuf mutex."))?; + .map_err(|err| format_err!("SendCell Mutex: {}", err))?; let px = m.try_get().ok_or_else(|| { format_err!("Pixbuf was accessed from a different thread than created") })?;