diff --git a/fractal-api/src/backend/mod.rs b/fractal-api/src/backend/mod.rs index 6f3b1440..2247f7dc 100644 --- a/fractal-api/src/backend/mod.rs +++ b/fractal-api/src/backend/mod.rs @@ -1,6 +1,6 @@ extern crate url; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Condvar}; use std::thread; use self::url::Url; use std::sync::mpsc::{Sender, Receiver}; @@ -45,6 +45,7 @@ impl Backend { internal_tx: None, data: Arc::new(Mutex::new(data)), user_info_cache: CacheMap::new().timeout(60*60), + limit_threads: Arc::new((Mutex::new(0u8), Condvar::new())), } } diff --git a/fractal-api/src/backend/types.rs b/fractal-api/src/backend/types.rs index b620ca50..27cf44bd 100644 --- a/fractal-api/src/backend/types.rs +++ b/fractal-api/src/backend/types.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, Condvar}; use std::sync::mpsc::Sender; use error::Error; @@ -133,6 +133,8 @@ pub struct Backend { // user info cache, uid -> (name, avatar) pub user_info_cache: CacheMap>>, + // semaphore to limit the number of threads downloading images + pub limit_threads: Arc<(Mutex, Condvar)>, } impl Clone for Backend { @@ -142,6 +144,7 @@ impl Clone for Backend { data: self.data.clone(), internal_tx: self.internal_tx.clone(), user_info_cache: self.user_info_cache.clone(), + limit_threads: self.limit_threads.clone(), } } } diff --git a/fractal-api/src/backend/user.rs b/fractal-api/src/backend/user.rs index d224738e..d561afa6 100644 --- a/fractal-api/src/backend/user.rs +++ b/fractal-api/src/backend/user.rs @@ -101,7 +101,21 @@ pub fn get_avatar_async(bk: &Backend, member: Option, tx: Sender let alias = m.get_alias().clone(); let avatar = m.avatar.clone(); + let thread_count = bk.limit_threads.clone(); thread::spawn(move || { + // waiting, less than 20 threads at the same time + // this is a semaphore + // TODO: use std::sync::Semaphore when it's on stable version + // https://doc.rust-lang.org/1.1.0/std/sync/struct.Semaphore.html + let &(ref num, ref cvar) = &*thread_count; + { + let mut start = num.lock().unwrap(); + while *start >= 20 { + start = cvar.wait(start).unwrap() + } + *start += 1; + } + match get_user_avatar_img(&baseu, uid, alias.unwrap_or_default(), avatar.unwrap_or_default()) { Ok(fname) => { tx.send(fname.clone()).unwrap(); @@ -110,6 +124,13 @@ pub fn get_avatar_async(bk: &Backend, member: Option, tx: Sender tx.send(String::new()).unwrap(); } } + + // freeing the cvar for new threads + { + let mut counter = num.lock().unwrap(); + *counter -= 1; + } + cvar.notify_one(); }); Ok(()) diff --git a/fractal-gtk/src/widgets/member.rs b/fractal-gtk/src/widgets/member.rs index ba8f241f..46194724 100644 --- a/fractal-gtk/src/widgets/member.rs +++ b/fractal-gtk/src/widgets/member.rs @@ -36,7 +36,7 @@ impl<'a> MemberBox<'a> { username.set_text(&self.member.get_alias().unwrap_or_default()); let avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 3); - get_member_avatar(backend.clone(), avatar.clone(), Some(self.member.clone()), 30); + get_member_avatar(backend.clone(), avatar.clone(), Some(self.member.clone()), 30, 3); avatar.set_alignment(0.5, 0.); w.pack_start(&avatar, false, false, 5); @@ -48,7 +48,11 @@ impl<'a> MemberBox<'a> { } } -pub fn get_member_avatar(backend: Sender, img: gtk::Image, m: Option, size: i32) { +pub fn get_member_avatar(backend: Sender, img: gtk::Image, m: Option, size: i32, tries: i32) { + if tries <= 0 { + return; + } + let (tx, rx): (Sender, Receiver) = channel(); backend.send(BKCommand::GetAvatarAsync(m.clone(), tx)).unwrap(); gtk::timeout_add(50, move || match rx.try_recv() { @@ -59,7 +63,7 @@ pub fn get_member_avatar(backend: Sender, img: gtk::Image, m: Option< } else { // trying again if fail img.set_from_icon_name("avatar-default-symbolic", 5); - get_member_avatar(backend.clone(), img.clone(), m.clone(), size); + get_member_avatar(backend.clone(), img.clone(), m.clone(), size, tries - 1); } gtk::Continue(false) diff --git a/fractal-gtk/src/widgets/message.rs b/fractal-gtk/src/widgets/message.rs index ec14ef14..00154528 100644 --- a/fractal-gtk/src/widgets/message.rs +++ b/fractal-gtk/src/widgets/message.rs @@ -127,7 +127,7 @@ impl<'a> MessageBox<'a> { avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 5); } - get_member_avatar(backend.clone(), avatar.clone(), m.cloned(), 40); + get_member_avatar(backend.clone(), avatar.clone(), m.cloned(), 40, 3); avatar.set_alignment(0.5, 0.); avatar